100% found this document useful (1 vote)
10 views

Salesforce+Development+Notes

The document outlines a comprehensive curriculum on Salesforce and cloud computing, covering topics such as cloud definitions, data centers, Salesforce architecture, object-oriented programming concepts, data types, classes, methods, and SOQL. Each day focuses on specific aspects of Salesforce development, including practical assignments and resources for further learning. The content is structured to guide learners through foundational concepts to more advanced topics in Salesforce programming.

Uploaded by

aaditya31001
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
10 views

Salesforce+Development+Notes

The document outlines a comprehensive curriculum on Salesforce and cloud computing, covering topics such as cloud definitions, data centers, Salesforce architecture, object-oriented programming concepts, data types, classes, methods, and SOQL. Each day focuses on specific aspects of Salesforce development, including practical assignments and resources for further learning. The content is structured to guide learners through foundational concepts to more advanced topics in Salesforce programming.

Uploaded by

aaditya31001
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 301

DAY1 - What is Cloud.................................................................................................................

4
Recording Link...................................................................................................................... 4
Data Centres......................................................................................................................... 4
Cloud Computing vs Traditional Computing.................................................................. 6
What is Salesforce............................................................................................................... 7
What is Multi Tenant Architecture....................................................................................7
OOPs Concept in Salesforce..............................................................................................8
Objects............................................................................................................................. 8

ls
Classes............................................................................................................................10
Abstraction (What the object does)......................................................................... 10

oo
Inheritance......................................................................................................................11
Polymorphism................................................................................................................ 11
Encapsulation (How the Object does) - Developers/Architect...........................12
ch
Resources....................................................................................................................... 13
Day2 - Data Types in Salesforce Apex................................................................................. 14
Recording Link.....................................................................................................................14
Data types and variables.................................................................................................. 14
rS

Primitive data type............................................................................................................. 14


Non-primitive data type.................................................................................................... 15
Assignment...........................................................................................................................17
he

Day 3 Classes in Salesforce....................................................................................................17


Recording Link..................................................................................................................... 17
Classes in Salesforce......................................................................................................... 17
nt

Access Modifiers in Salesforce..................................................................................18


Variables(States) in Salesforce..................................................................................19
Resources.......................................................................................................................19
Pa

Methods in Salesforce...................................................................................................... 20
Day 4 - Static vs Non-static methods................................................................................. 21
Recording Link..................................................................................................................... 21
Methods Continue.............................................................................................................. 21
Static Methods....................................................................................................................22
Assignment.......................................................................................................................... 23
Resources............................................................................................................................ 24
Day 5 - Constructors in Salesforce......................................................................................24
Recording Link.................................................................................................................... 24
Example of Constructor................................................................................................... 24
Types of Constructors.......................................................................................................25
Default Constructor.....................................................................................................25
Parameterized Constructor....................................................................................... 26
This Keyword in Salesforce.............................................................................................. 27
Create the Instance of Apex Class when the constructor is private..................... 28
Assignment..........................................................................................................................29
Resources............................................................................................................................ 30
Day6 - Operators in Salesforce.............................................................................................30
Recording Link.................................................................................................................... 30
Arithmetic Operator........................................................................................................... 31
Conditional Statements.................................................................................................... 31

ls
Logical Operator ( &&, || )..................................................................................................34
Switch Statement.............................................................................................................. 34

oo
Resources............................................................................................................................ 36
Day7 - Operators Continue.................................................................................................... 36
Recording Link.................................................................................................................... 36
ch
Increment & Decrement Operator ( ++, - )...................................................................36
Ternary Operator ( ? : )......................................................................................................37
Null Coalescing Operator ( ?? )....................................................................................... 37
Constants.............................................................................................................................37
rS
Day8 - Instance block vs Static Block & Call Stack in Salesforce................................38
Recording Link.................................................................................................................... 38
Instance Block / Object Block.........................................................................................38
he

Static Block / Class Block................................................................................................39


Call Stack in Salesforce................................................................................................... 40
Scopes in Salesforce..........................................................................................................41
DAY9 - Collections in Salesforce.......................................................................................... 42
nt

Recording Link.............................................................................................................. 42
Type of Collections......................................................................................................42
Pa

Methods of List Collection in Salesforce............................................................... 45


Assignment.................................................................................................................... 47
DAY10 - Arrays & Set in Salesforce.......................................................................................47
Recording Link.................................................................................................................... 47
Arrays in Salesforce........................................................................................................... 47
Sets in Salesforce..............................................................................................................48
When to Use Set................................................................................................................ 49
Assignment..........................................................................................................................50
DAY11 - Map Collection in Salesforce Part 1.......................................................................50
Recording Link.................................................................................................................... 50
Map in Salesforce.............................................................................................................. 50
Structure of the Map Apex............................................................................................... 51
Methods in Salesforce Map............................................................................................. 52
Resources............................................................................................................................ 53
DAY12 - Map Collection in Salesforce Part 2..................................................................... 54
Recording............................................................................................................................. 54
Assignment..........................................................................................................................56
Resources............................................................................................................................ 56
Day13 - Looping Statement in Salesforce...........................................................................57
Recording Link.................................................................................................................... 57
Loops in Salesforce........................................................................................................... 57

ls
1. The traditional loop................................................................................................. 57
2. The list or set iteration for the loop................................................................... 58

oo
3. The SOQL for loop.................................................................................................. 59
While Loop.......................................................................................................................... 60
Assignment..........................................................................................................................62
ch
Resources............................................................................................................................ 62
DAY14 - Getter/Setter in Salesforce..................................................................................... 63
Recording Link.................................................................................................................... 63
Definition............................................................................................................................. 63
rS
Call by Reference & Call by Value.................................................................................. 65
Call by Value in Apex.................................................................................................. 65
Call by Reference in Apex.......................................................................................... 65
he

DAY15 - Introduction to SOQL in Salesforce...................................................................... 67


Recording Link.................................................................................................................... 67
Definition..............................................................................................................................67
Syntax................................................................................................................................... 67
nt

Assignment..........................................................................................................................68
Resources............................................................................................................................ 69
Pa

DAY16 - SOQL Continue.......................................................................................................... 69


Recording Link.................................................................................................................... 69
SOQL Continue................................................................................................................... 69
SOQL Structure.................................................................................................................. 70
Queries we developed in the class................................................................................ 70
DAY17 - SOQL Continue.......................................................................................................... 72
Recording Link.................................................................................................................... 72
Group BY in Salesforce..................................................................................................... 72
Having clause in Salesforce............................................................................................. 72
Child to Parent SOQL Query............................................................................................ 72
Parent to Child SOQL Query............................................................................................ 72
DAY1 - What is Cloud

ls
Recording Link
Link - https://youtu.be/ROw_bQQ4vZc

oo
In Simple terms, the Cloud is someone else's computer that can be accessed on
demand via the internet. We can also say that the Cloud is the vast connections of
various servers to store the data.
ch
The Cloud is used to
● Store
● Manage
rS
● Process

Data Centres
When we talk about Clouds these are not the digital cyberspace floating on the air.
he

The data centres are the physical servers which are used to store the data. There
are multiple racks aligned inside the data centres and you can consider the racks
as the storage for different applications which are separate and secure.
nt
Pa
ls
oo
ch
rS
he
nt

Data Centers do consume a huge amount of electricity and this electricity can be
used to light a small village.
Pa

These data centres do heat like your personal computers and to keep these data
centres cold these are built in the cold places or cooled using a mechanism like
the Chilled Water System.
ls
oo
ch
rS
he
nt

Cloud Computing vs Traditional Computing


Pa

Cloud computing involves accessing and using computing resources (like storage
and processing power) over the internet, paying for what you use. Traditional
computing relies on physical servers and infrastructure managed on-site. In
simple terms, cloud computing is like renting computing services online, while
traditional computing involves owning and managing your own hardware.

What is Salesforce
SaaS revolutionised software delivery, offering on-demand access over the internet
through subscriptions. Companies no longer required purchasing and maintaining
software and hardware; instead, they could access everything online. It was akin to
shifting from DVD purchases to streaming movies on Netflix.

Salesforce works on a Multi Tenant Architecture.

What is Multi Tenant Architecture

ls
oo
ch
rS
he
nt
Pa

OOPs Concept in Salesforce

● OOPs Concept
○ Object
○ Class
● Abstraction
● Inheritance
● Polymorphism
● Encapsulation

Objects

→ Any real-world entity that has a state and behaviour is known as an object. For
example, a chair, pen, table, keyboard, bike, etc.
→ It can be physical or logical.
→ A Cat is an Object because it has colour, height, and weight as state and speaks,
walks, and sleeps as behaviour

ls
→ A table or chair is also an object because it has colour, height, width &, etc.

oo
ch
rS
he
nt
Pa
ls
Classes

oo
→ Collection of Objects is a class. For Example, Vehicles, Tree, Animal
→ Class can also be defined as a blueprint from which we can create multiple
individual objects.
ch
rS
he
nt

Abstraction (What the object does)


Pa

Hiding internal (implementation) details and exposing/showcasing only the


functionality is known as abstraction.
For example tv, We do not what happens behind the scene
ls
oo
ch
Inheritance

When a child object acquires all the properties (variables/methods) from a parent
object then it is known as inheritance.
rS
he
nt
Pa
Polymorphism

When the same task is done in a different way then it is known as Polymorphism.
For Example, speaking, walking, convincing customers, playing football, YouTube
sessions, teaching
● Method Overloading - Static Polymorphism
● Method Overriding - Dynamic ( RunTime ) Polymorphism

ls
oo
ch
rS
he

Encapsulation (How the Object does) - Developers/Architect

Wrapping hiding/code (Putting together) and data together into a single unit are
known as encapsulation.
nt

For example, a capsule is wrapped with different medicines.


In Salesforce, class is a perfect example of encapsulation.
Pa
ls
oo


ch
DRY Principle
○ Do not repeat yourself
SOLID Principle (Intro)
○ S - Single Responsibility Principle (SRP)
rS
○ O - Open-Closed Principle
○ L - Liskov’s Substitution Principle
○ I - Interface Segregation Principle
○ D - Dependency Inversion Principle
he

Resources

● https://www.javatpoint.com/java-oops-concepts
● https://www.geeksforgeeks.org/object-oriented-programming-oops-concept-
nt

in-java/
● https://www.mygreatlearning.com/blog/oops-concepts-in-java/
● https://medium.com/from-the-scratch/oop-everything-you-need-to-know-a
Pa

bout-object-oriented-programming-aee3c18e281b
● https://medium.com/edureka/object-oriented-programming-b29cfd50eca0
● https://medium.com/backticks-tildes/the-s-o-l-i-d-principles-in-pictures-b
34ce2f1e898
Day2 - Data Types in Salesforce Apex

Recording Link
https://youtu.be/ZW6Xf1IuFpg

Data types and variables

● Primitive data type


● Non-primitive data type

Data Types - Before we talk about data types let’s take an example of the given

ls
mathematical equation

y + x2 + 7 = 10 ⇒ Number

oo
X Is a very good Manager. ⇒ String

In this equation, there are variables x & y which are used to solve the given
ch
equation. Now, the question that you need to ask is what is the type of data that
will be stored in the given variables x & y.

Primitive data type


rS

They are simple, basic data types that are built into the Salesforce platform and
cannot be broken down into smaller components.
he

● Integer: A whole number without a decimal point.


● Double: A number with a decimal point or a fractional component.
● Boolean: A data type that can have one of two possible values - true or
false.
nt

● Date: A data type that represents a date.


● Time: A data type that represents a specific time.
● DateTime: A data type that represents a specific date and time.
String: A sequence of characters that can include letters, numbers, and
Pa


symbols.
● ID: A unique identifier for an object in Salesforce. ( 18, 15 ) 001 GHJ HE87734
● Decimal: A number with a decimal point, with up to 18 digits. Example -
90.67766
● Long

Non-primitive data type

In Salesforce, non-primitive data types are also known as complex data types or
custom/standard objects.

The non-primitive data types in Salesforce include:


● Object - Class
○ Super Class
● SObject Class
● Standard Object
● Custom Object
● Map
● List
● Set
● Apex Class
○ Multiple Variables / States

ls
oo
ch
rS
he
nt
Pa

// <DataType> nameOfVariable; // Variable Declaration


// <DataType> nameOfVariable = null;
// Variable Declaration with Value Assignment
// <DataType> nameOfVariable = DefaultValue;
// Variable Declaration with DefaultValue Assignment

// <DataType> nameOfVariable;
String name; // null
Integer age = 30;
Boolean isAdult = false;
Decimal salary = 278423.432;
Date dateOfBirth = System.today();
DateTime orderCreatedDate = System.now();
Id recordId = '006dL000000Hty9QAC';
Opportunity opportunityRecord; // null
Account accountRecord; // null
SObject sobjectRecord; // null
Object objRecord; // null
JWT jwt; // null

ls
Assignment

Create variables for all the Primitive Data Types and print those with dummy

oo
values.

ch
Day 3 Classes in Salesforce

Recording Link
rS
Link - https://youtu.be/iSGwlx32yQ8

Classes in Salesforce

A class is the blueprint that is used to create object(s) for the classes. A Class
he

contains the following

● Variable
● Methods
nt

● Constructors

Structure of the Apex Class


Pa

public class Animal{

Where

● Public is the access modifiers


● class is the keyword used to specify that this is a class
● Animal is the name of the Apex class
As we have used the word Access Modifiers, before we talk about the Apex Class,
It’s very important to learn/know about what are the access modifiers and what
are the different use cases for Access Modifiers.

<access_modifier> class <ClassName>{

Access Modifiers in Salesforce

ls
Access modifiers in Salesforce are used to define the visibility and accessibility of
classes, methods, variables, and other components within an organisation.

oo
There are three types of access modifiers in Salesforce:

1. Public: Components with the public access modifier are visible and
accessible to all classes and triggers in an organisation. This means that any
ch
class or trigger in the organisation can access and use these components.
2. Private: Components with the private access modifier are only visible and
accessible within the same class in which they are defined. This means that
no other class or trigger in the organisation can access or use these
rS
components.
3. Protected: Components with the protected access modifier are visible and
accessible within the same class and any subclasses that extend the
original class. This means that any class that inherits from the original class
can access and use these components.
he

a. Class A (Parent)
i. Protected Variables
b. Class B (Child ) extend Class A (Variable & Methods)
i. Access Protected Variables
nt

4. Global
a. Managed Package ( Application ) ~= Applications in Google/Apple
b. API Development
Pa

public class Animal {


public String name; // private
Integer age;
String color;
}

//<classname> <instanceName> = (assignment operator) new (keyword)


constructor
Animal dog = new Animal();
//dot notation
dog.name = 'Max';
System.debug(dog.name);

Variables(States) in Salesforce

Variables in Salesforce are used to store and manipulate data within Apex code.

Assignment -

● Play around with Class Variable along with Access Modifiers

ls
● Create an Apex Class with the Name it “AccountManager” and add the
following variables
○ Bank Account Name

oo
○ Bank Account Number
○ Swift Code
○ and many more……

Resources


ch
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_data_types.htm
rS
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_understanding.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_access_modifiers.htm
he
nt
Pa

Methods in Salesforce
In Salesforce, a method is a block of code that performs a specific task or action.
Methods can be used to perform calculations, manipulate data, and interact with
the Salesforce platform, such as by creating, updating, or deleting records.

<Access Modifier> <Return Type> <Method Name> (<Optional Parameters>) {


// Method Body
}
public static Integer calculateSum(Integer a, Integer b) {
return a + b;
}
public void calculateSum() {
}
public Integer calculateSum(Integer a, Integer b) {
return a + b;
}

ls
There are two types of methods in Salesforce:

1. Instance/Object Methods: Instance methods are associated with an

oo
instance(Object) of a class, and can be called on that instance.
2. Static(Class) Methods: Static methods are not associated with an instance
of a class, and can be called on the class itself. Static methods are used to
perform actions that do not require an instance of the class.
ch
public class Animal {
public String name; // public
Integer age;
rS
String color;

/*
<Access Modifier> <Return Type> <Method Name> (<Optional
he

Parameters>) {
// Method Body
}
*/
nt

public void printName(){


System.debug('The Name of the Dog is, '+name); // The Name of
the Dog is, Max
System.debug(name+', is the Name of the Dog.'); // Max, is the
Pa

Name of the Dog.


printAge();
}

void printAge(){
System.debug(age);
}
}

Animal dog = new Animal();


dog.name = 'Max';
System.debug(dog.name);
// Call the method
dog.printName();

Day 4 - Static vs Non-static methods

Recording Link
https://youtu.be/TUUK18EZlA8

ls
Methods Continue

oo
ArithmaticOperations math = new ArithmaticOperations();
System.debug('Instance value is '+math);
ch
math.sum(23,54); // arguments
Integer sum = math.sum(23,54,65); // arguments
System.debug(sum);
rS

public class ArithmaticOperations {

public void sum(Integer number1, Integer number2){ // parameters


he

Integer sum = number1 + number2;


System.debug('The Sum of two numbers is '+sum);
}
nt

public Integer sum(Integer number1, Integer number2, Integer


number3){
Integer sum = number1 + number2 + number3;
System.debug('The Sum of 3 numbers is '+sum);
Pa

return sum;
}
}

Static Methods

public class ArithmaticOperations {


/* Instance Variable/Object Variable */
public String name = 'Amit Singh';

/* Class Variable/Static Variable */


public static Integer multiplication = 345;

/* Class Methods/Static Methods */


public static void multiply(Integer number1, Integer number2){
//System.debug(name);
//sum(34,34);
if(number1 <= 0){

ls
System.debug(' Retrun Nothing ');
return; // return nothing;
}

oo
Integer mul = number1 * number2;
System.debug('multiply outcome '+mul);
}

ch
/* Instance Methods/Object Methods */
public void sum(Integer number1, Integer number2){ // parameters
System.debug(name);
if(number1 <= 0){
rS
System.debug(' Retrun Nothing ');
return; // return nothing;
}
multiply(number1, number2);
he

Integer sum = number1 + number2;


System.debug('The Sum of two numbers is '+sum);
return;
}
nt

public Integer sum(Integer number1, Integer number2, Integer


number3){
Integer sum = number1 + number2 + number3;
Pa

System.debug('The Sum of 3 numbers is '+sum);


return sum;
}
}

ArithmaticOperations math = new ArithmaticOperations();


System.debug(math);

ArithmaticOperations.multiply(30, 10);
ArithmaticOperations.multiply(-30, 10);
Assignment
1. Create an Instance method sum method with different parameters. Note: -
Please use the return statement accordingly
a. sum(1,2)
b. sum(3,4,6)
c. sum(3,4,6,4)
d. sum(3,4,6,5,6)
e. sum(3,4,6,7,8,8)
f. sum(3,4,6...)
g. sum(3,4,6..)
h. sum(3,4,6)

ls
i. sum(3,4,6)
j. sum(3,4,6)
k. sum(3,4,6)

oo
l. sum(3,4,6)
2. Create Class/Static methods for multiple/sub/div.
a. Sub() - 2, 3, 4, 5, ------- up to
b. mul() - 2, 3,4 ------
ch
c. div(23, 45)
d. module() - 29/25 ~= 4
rS

Resources
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
he

apex_classes_defining_methods.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_static.htm
nt

Day 5 - Constructors in Salesforce

Recording Link
Pa

Link - https://youtu.be/Gw8Q78ix5Kc

A constructor is a unique method that is used to create and initialise an


instance/Object of a class. When an object is created from a class, the constructor
is automatically called to initialise the object's state and properties.

● The constructor does not have any return type.


● The name of the constructor must be the same as Class Name.
● The parent class(topmost class) constructor should always be public.
○ Private in cases where the methods are class methods.
Example of Constructor

public class Animal {

}
Animal dog = new Animal();

Where -
Animal - Class
dog - the variable name of the Object
= Assignment operator
new - keyword

ls
Animal() - constructor

oo
Types of Constructors
ch
There are two types of constructors in Salesforce:
rS
● Default Constructor: A default constructor is a constructor that is
automatically created by Salesforce if no other constructor is defined in the
class.
○ The default constructor takes no arguments and does not perform
any initialization.
he

● A parameterized constructor is a constructor that takes one or more


parameters and uses them to initialise the object's state and properties.
○ When you want to enforce that, the user must need to pass the value
nt

while creating the instance/Object.


○ Parameterized constructors are useful for setting initial values or
default values for object properties.
■ Animal dog = new Animal(‘Paremrian’);
Pa

Default Constructor

public class Vehicle {

private Vehicle(){
System.debug('Constructor is Executed!');
// SOQL Query is Allowed
// DML - Not Allowed/ Not Recommended
}

public void Vehicle(){


System.debug('Method is Executed!');
}
public void printName(){

}
}

Vehicle car = new Vehicle(); // default


System.debug('1');

ls
oo
Parameterized Constructor

public class Vehicle {

ch
public Vehicle(){
System.debug('Constructor is Executed!');
// SOQL Query is Allowed
// DML - Not Allowed/ Not Recommended
rS
}
public Vehicle(String makeYear){
System.debug(makeYear);
}
public Vehicle(String makeYear, String companyName){
he

}
public Vehicle(Decimal minPrice){
nt

}
public Vehicle(String makeYear, String modelName, String
companyName){
Pa

public void Vehicle(){


System.debug('Method is Executed!');
}
public void printName(){

}
}
Vehicle car = new Vehicle(); // default
System.debug('1');
Vehicle bike = new Vehicle('2024');

This Keyword in Salesforce


● this keyword represents the instance of an Apex Class
● You can use this keyword to access any instance methods and variables
● this keyword will be used to differentiate between the instance variables
and method/constructor arguments

ls
public class Vehicle {

oo
String makeYear;
Decimal minimumPrice;
String modelName;
ch
public static String companyName;

public Vehicle(){
rS
System.debug('Constructor is Executed!');
// SOQL Query is Allowed
// DML - Not Allowed/ Not Recommended
}
he

public Vehicle(String makeYear){


System.debug(makeYear);
// Vehicle v = new Vehicle(); // Creating the instance
// this keyword
nt

System.debug(this);
this.makeYear = makeYear;
System.debug('From Instance Variable '+this.makeYear);
}
Pa

public Vehicle(String makeYear, String companyName){


Vehicle.companyName = companyName;
}
public Vehicle(Decimal minPrice){
this.minimumPrice = minPrice;
}
public Vehicle(String makeYear, String modelName, String
companyName){

public void Vehicle(){


System.debug('Method is Executed!');
}
public void printName(){

}
}

ls
Create the Instance of Apex Class when the constructor is
private

oo
public class Vehicle {

String makeYear;
ch
Decimal minimumPrice;
String modelName;
rS
public static String companyName;

public static Vehicle getInstance(){


return new Vehicle();
}
he

private Vehicle(){
System.debug('Constructor is Executed!');
// SOQL Query is Allowed
nt

// DML - Not Allowed/ Not Recommended


}
Pa

public void Vehicle(){


System.debug('Method is Executed!');
}
public void printName(){

}
}

// Execute the below code from Anonymous Window


Vehicle car = Vehicle.getInstance();
System.debug(car);
Pa
nt
he
rS
ch
oo
ls
Assignment

Create a class “Car” that should have the following

● The class should have the private variables


○ Name
○ Model
○ Year
○ Company
○ Type
● Create multiple constructors that are accepting the different parameters
○ Car(String name, String type)

ls
○ Car(String name)
○ Car( )

oo
○ Car(String name, String model, String year, String company, Type)
○ Car(Integer year)
● Within the constructors assign the values to the private variables. Note: -
The value should be assigned to the variable that is passed in the
ch
constructor and for other variables assign a blank value. See the example
below
○ public Car(String year){
this.year = year;
rS
this.name = '';
this.model = '';
this.company = '';
this.type = '';
}
he

● Create various methods to print the Car Details


○ printCarInformation( ) → This method should print all the information
about the Car
nt

○ printCarName( ) → This method should only print the name of the Car
○ printCarYear( ) → This method should only print the built year of the
Car
○ printCarNameType( ) → This method should only print the name &
Pa

type of the Car

Resources

● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_constructors.htm
● https://www.tutorialkart.com/learn_apex/constructor-in-apex-programming/

Day6 - Operators in Salesforce


Recording Link
https://youtu.be/gTQiMbpJcow

Operators are used to perform certain operations in Salesforce Apex Code.


Salesforce has provided various operators for us to use them. Some of them are
as below

● Arithmetic Operator ( +, -, /, * )
● Assignment operators ( =, +=, -=, *=, /= )
● Conditional Statements ( Not Part of Operators )
● Ternary Operator ( ? : )
● Logical Operator ( &&, || )

ls
● Equality Operator ( == )
● Increment & Decrement Operator ( ++, – )

oo
● Safe navigation operator ( ?. )
● Null Coalescing Operator ( ?? )

ch
rS
he
nt
Pa
Arithmetic Operator

// +, -, *, /, %
Integer number1 = 33;
Integer number2 = 10;
Integer remainder = Math.mod(number1, number2);
System.debug(remainder);
//Math Class

Conditional Statements

ls
● If else
● Switch

oo
Use the "if-else" statement in Apex code to execute different blocks of code
based on a condition

ch
Integer age = 90;
// age is greater than or equal to 60
// >, <, >=, <=
if(age >= 60){
rS
System.debug('Old Age');
}else{
System.debug('Not Old Age');
}
he

Integer age = 40;


// age is greater than or equal to 60
// >, <, >=, <=
nt

if(age >= 60){


System.debug('Old Age');
}
// age >= 18 AND age <=40
Pa

// && ( Logical AND )


else if(age >= 18 && age <=40){
System.debug('Youth');
} else {
System.debug('Minor');
}

Integer age = 17;


if(age >= 60){ // True
System.debug('Old Age');
}
if(age >= 18 && age <=40){
System.debug('Youth');
}
if(age >= 60 && age <=60){

Integer age = 17;


if(age >= 60){ // True
System.debug('Old Age');

ls
}
// age >= 18 && age <=40
// OR

oo
// age >= 40 && age <= 60
if( (age >= 18 && age <=40) || (age >= 40 && age <= 60) ){
System.debug('Youth');
} ch
if(age >= 60 && age <=60){
rS
}

if (condition) {
he

// code block to execute if the condition is true


}

if (condition) {
nt

// code block to execute if the condition is true


}

if (condition) {
Pa

// code block to execute if the condition is true


} else {
// code block to execute if the condition is false
}

if (condition) { // boolean (TRUE/FALSE)


// code block to execute if the condition is true
} else if(condition) {
// code block to execute if the condition is false
} else {
}

// Example 1

if (condition) { // boolean (TRUE/FALSE)


// code block to execute if the condition is true
} else if(condition) {
// code block to execute if the condition is false
} else {

ls
}

oo
// Example 2

String fruit = 'banana';


ch
if (fruit == 'apple') {
System.debug('This is an apple');
} else if (fruit == 'orange') {
System.debug('This is an orange');
rS
} else {
System.debug('This is neither an apple nor an orange');
}
he

Logical Operator ( &&, || )


nt

Integer age = 17;


if(age >= 60){ // True
Pa

System.debug('Old Age');
}
// age >= 18 && age <=40
// OR
// age >= 40 && age <= 60
if( (age >= 18 && age <=40) || (age >= 40 && age <= 60) ){
System.debug('Youth');
}

if(age >= 60 && age <=60){

}
Switch Statement
You can use the "switch" statement in Apex code to execute different blocks of
code based on the value of a variable.

The syntax for the "switch" statement is as follows:

switch on expression {
when value1 {
// code block to execute if expression equals value1

ls
}
when value2 {

oo
// code block to execute if expression equals value2
}
when value3 {
// code block to execute if expression equals value3
} ch
when else {
// code block to execute if the expression does not equal any of the
specified values
rS
}
}

String fruit = 'banana';


he

switch on fruit {
when 'apple' {
System.debug('This is an apple');
}
nt

when 'orange' {
System.debug('This is an orange');
}
Pa

when 'BaNaNa' {
System.debug('This is a banana');
}
when else {
System.debug('This is neither an apple nor an orange');
}
}

Here's an example of how to use the "switch" statement in Apex code:

String fruit = 'banana';


switch on fruit {
when 'apple' {
System.debug('This is an apple');
}
when 'orange' {
System.debug('This is an orange');
}
when 'banana' {
System.debug('This is a banana');
}
when else {
System.debug('This is not an apple, orange, or banana');
}

ls
}

oo
Resources


ch
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_if_else.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_switch.htm
rS
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_expressions_operators_understanding.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_SafeNavigationOperator.htm
he

● Maths Class -
https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_math.htm
● String Class -
nt

https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_string.htm
Pa

Day7 - Operators Continue

Recording Link
https://youtu.be/wXWzyjg08u4

Increment & Decrement Operator ( ++, - )

Integer i = 5;
// i++ = i = i + 1
if(6 <= i++){
System.debug('6 <= i++');
}
//++i; // Pre Increment
System.debug(i); // 6
if(7 <= ++i){
System.debug('7 <= ++i');
}
System.debug(i);

Integer j = 10;
Integer x = 45;

ls
j += x;
//j -= x;
//j *= x;

oo
System.debug(j);

String message = 'Hello, Welcome to the world of';


//message = message + ' Salesforce Apex';
ch
message += ' Salesforce Apex;';
System.debug(message);

Ternary Operator ( ? : )
rS

Integer age = 19;


String outcome;
if(age > 18){
he

outcome = 'adult';
}else {
outcome = 'not adult';
}
nt

System.debug(outcome);
String message = (age > 18) ? 'adult' : 'not adult';
System.debug(message);
Pa

Null Coalescing Operator ( ?? )

Integer age = 19; // null


/*if(age == null){
age = 30;
}*/
age = age ?? 30;
System.debug(age); // 19
String message;
message = message ?? 'Hello There';
System.debug(message);

Account acc;
System.debug('Before '+acc);
acc = acc ?? new Account();
System.debug('After '+acc);

Constants

In Salesforce, you can define constants using the "final" keyword in Apex code.

ls
Constants are variables whose values cannot be changed once they are initialised.
public class MyConstants {

oo
public static final Decimal PI_VALUE = 3.14;
public static final Decimal GRVT_VALUE = null;
}

ch
System.debug(MyConstants.PI_VALUE);
// System.FinalException: Final variable has already been initialised
//MyConstants.PI_VALUE = 9.8;
MyConstants.GRVT_VALUE = 9.8; //initialising
rS
System.debug(MyConstants.GRVT_VALUE);
//MyConstants.GRVT_VALUE = 9.8;
he

Day8 - Instance block vs Static Block & Call Stack in


Salesforce
nt

Recording Link
https://youtu.be/zomZpCzqNks
Pa

Instance Block / Object Block


An instance block in Salesforce is also similar to Java, as it is a block of code that
is executed each time an instance of the class is created. It is often used to
initialise instance variables or to perform any other instance-specific initialization
tasks.

Note:- There could be more than one instance block in a Salesforce Apex Class

// Instance block executed


// Constructor executed
public class CurrencyManager {
String name;
static String address;
{
System.debug('Instance block');
name = 'Amit Singh';
address = '123 Main Street';
}
public CurrencyManager(){
System.debug('Constructor') ;
}
}

ls
Static Block / Class Block

oo
A static block in Salesforce is a block of code that is executed only once, when the
class is loaded into memory, just like in Java. It is typically used to initialise static
variables or to perform any other static initialization tasks.
ch
public class CurrencyManager {
rS
String name;
static String address;
static Integer age = 10;
{
System.debug(name);
he

System.debug('Instance block');
name = 'Amit Singh';
address = '123 Main Street';
}
nt

static {
System.debug(age);
address = '123 Main Street...';
Pa

System.debug('Static block');
System.debug(address);
}
public CurrencyManager(){
System.debug('Constructor') ;
}
}
Call Stack in Salesforce

Heap Memory - Variables / Objects


Stack Memory - Methods ( LIFO - Last in First Out)

In Salesforce, as in any other programming language, a call stack is a data

ls
structure that keeps track of the sequence of method calls during the execution of
a program.

oo
When a method is called, the program pushes a new frame onto the call stack to
store information about the method's arguments, local variables, and return
address. When the method returns, the frame is popped from the call stack, and
ch
the program resumes execution from the return address stored in the popped
frame.

The call stack in Salesforce can be particularly important in debugging, as it can


rS
help developers identify where an error occurred and how the program reached
that point.

Link to PPT -
https://docs.google.com/presentation/d/1E2lYvdyaqn0Bn4D1_Ybv44S8zW203rmReEr
he

JVPxn3_g/edit?usp=sharing

public class CallStack {


nt

public void methodA(){


System.debug('A is Executed!');
Pa

methodB();
}
public void methodB(){
System.debug('B is Executed!');
methodC();
}
public void methodC(){
System.debug('C is Executed!');
methodD();
}
public void methodD(){
System.debug('D is Executed!');
methodE();
}
public void methodE(){
System.debug('E is Executed!');
}
}

public class CallStackDemo {


public void methodA(){
methodE();
methodB();

ls
methodD();
System.debug('A is Executed!');
}

oo
public void methodB(){
methodC();
System.debug('B is Executed!');
} ch
public void methodC(){
System.debug('C is Executed!');
}
rS
public void methodD(){
System.debug('D is Executed!');
}
public void methodE(){
System.debug('E is Executed!');
he

}
}
nt

Scopes in Salesforce
Pa

public class Scopes {


String employeeId;
{

public void sayHello(){


if(10 < 9)
System.debug('10 < 11');
System.debug('Executes always!');
String employeeId;
if(true){
employeeId = 'EMP-3454';
}
System.debug(employeeId);
}

public void greetings(){


//System.debug(employeeId);
}
}

ls
DAY9 - Collections in Salesforce
Recording Link

oo
https://youtu.be/DnEuBXkHID8

A collection is a data structure(Data Types) that is used to store multiple values of


ch
the same data type. Collections are used to manipulate large sets of data
efficiently and effectively.

String name;
rS
String name1;
String name2;
String name3;
..
he

..
..
..
..
nt

..
String nameN;

String fruit = ‘Apple’;


Pa

String fruitName = ‘Banana’;

Type of Collections

● List
● Set
● Map

List
A list is a collection of elements that are ordered and can be accessed using their
index. The elements of a list can be of any data type, including primitive and
non-primitive data types.

In Salesforce, lists are represented by the "List" keyword in Apex code and are
commonly used to store and manipulate large data sets.

There is a class named List.

List<Datatype> variabname = new List<Data Type>();

Lists allow for various operations such as adding or removing elements, iterating

ls
over elements, searching for elements, and sorting elements.

List of Index = Total Size of List(Total Elements in List) - 1

oo
Total Index = 4
First Index = 0
Last Index = 4-1

Index 0
ch Index 1 Index 2 Index 3

Apple Mango Orange Banana


rS
he
nt

This is how they can be created in Salesforce


Pa

// Syntax

#1 -
List<Datatype> variabname = new List<Data Type>();

// Declare & Memory Allocation


List<String> fruitList = new List<String>(); // Size = 0, Size = 1

// Declare & Memory Allocation & Value Assignment


#2 -
List<String> fruitList = new List<String>{'apple', 'banana', 'orange'};
List<String> orderStatusList = new List<String>{
'Created','Submit','In Process',
'Out for Delivery','Delivered',
'Return Requested','Return Pickup Scheduled',
'Retured', 'Refund Requested',
'Refunded'
};

List<Integer> numbersList = new List<Integer>{1,2,4,5,3};

// Declare the variable

ls
#3 -
List<String> fruitList; // null

oo
fruitList = new List<String>(); // Memory Allocation

fruitList = new List<String>{'apple', 'banana', 'orange'}; // Memory Allocation &


ch
Value Assignment

fruitList1 = new List<String>{'apple', 'banana', 'orange'};


rS
//List<DataType> variableName; // variable declaration
List<String> furitsList; // default value will be null
System.debug(furitsList);
// List<DataType> variableName = new List<DataType>();
he

List<String> furitsListNew = new List<String>();


System.debug(furitsListNew);
// List<DataType> variableName = new List<DataType>{'','','',''};
List<String> furits = new List<String>{'Orange', 'Apple','Banana',
nt

'Grapes'
};
System.debug(furits);
Pa

Methods of List Collection in Salesforce

There are numerous methods of List class in Salesforce, however, there are some
that are being used widely by the developers

List<Datatype> variabname = new List<Data Type>();

● add(element)
● set(index, element) - 1, ‘banana’
● get(index) - 0, 1, 2
● clear()
● clone()
● addAll(another set or list)
● remove(index)
● isEmpty()
● size()
● sort()
● Etc

For more information refer to this link -


https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_meth

ls
ods_system_list.htm

Use list when

oo
● Ordering the element is important
● Need of accessing the element by indexes
● Sorting is important


ch
Need to create records inside Salesforce sObjects
Need to store the records from the SOQL Queries
● You need to store multiple dimensions like Matrix
rS

List<String> fruits = new List<String>();


fruits.adD('Apple');
he

fruits.adD('Grapes');
fruits.adD('Orange');
System.debug(fruits);
List<String> fruitsList = fruits.clone();
nt

fruitsList.add('Banana');
System.debug(fruitsList);
System.debug('---'+fruits);
Pa

List<String> fruits = new List<String>();


fruits.adD('Apple');
fruits.adD('Grapes');
fruits.adD('Orange');
System.debug(fruits);
List<String> fruitsList = new List<String>();
fruitsList.add('Banana');
fruitsList.add('Guava');
fruitsList.add('Banana');
fruitsList.addAll(fruits);
System.debug(fruitsList);
System.debug('---'+fruits);

Assignment
1. Create a List to Store Some of the Salesforce Certifications
a. Playaround with the methods
2. Create a Set to store the Salesforce Certifications related to Salesforce
Developers
a. Playaround with the methods

ls
https://trailhead.salesforce.com/en/credentials/administratoroverview/

oo
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_collections.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_list.htm
● ch
https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_set.htm
rS

DAY10 - Arrays & Set in Salesforce


Recording Link
he

https://youtu.be/Lp3RWdy0v78

Arrays in Salesforce
nt

String[] fruits = new String[5];


System.debug(fruits); // outcome?
Pa

System.debug(fruits.size());
fruits[0] = 'Apple';
fruits.set(1, 'Grapes');
fruits.add('Banana'); // in the last of array/list
System.debug(fruits);
System.debug(fruits.size());
String name = fruits[0];
System.debug(name);

String[] fruits = new List<String>(); // blank


System.debug(fruits.size());
String[] fruitsList = new String[1];
System.debug(fruitsList.size());
fruits.add('Apple');

Sets in Salesforce

→ Set is an unordered set of collections elements.


→ Set does not contain the duplicate element
→ You can not access elements using indices
→ Below is visual representations of a set of String

ls
oo
ch
The set can be created in Salesforce like below

Set<Integer> colorsSet = new Set<Integer>();


rS
Set<Integer> colorsSet = new Set<Integer>{'Red', 'Black', 'Blue', 'Green'};
Set<Integer> colorsSet;

There are numerous methods of Set in Salesforce, however, there are some that
he

are being used widely by developers.

● add(element)
● contains(element)
nt

● addAll(another set or list)


● clear()
● remove(value)
Pa

● size()
● isEmpty()
● Ect

For more information refer to this link -


https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_meth
ods_system_set.htm

Set<Integer> ageSet;
Set<Decimal> salarySet = new Set<Decimal>();
Set<String> employeeIdsSet = new Set<String>{'1124','4543'};
salarySet.add(2345.34);
salarySet.add(2365.34);
salarySet.add(2341.34);
salarySet.add(2341.34);
salarySet.add(2341.34);
salarySet.add(1.34);
salarySet.add(2.34);
System.debug(salarySet);
Integer size = salarySet.size();
Boolean isEmpty = salarySet.isEmpty();
System.debug(size);
System.debug(isEmpty);
salarySet.remove(23653.34);

ls
System.debug(salarySet.size());
Boolean found = salarySet.contains(2.358);

oo
System.debug(found);
if(salarySet.contains(2.34)){
System.debug('Element Found!');
}else{

}
ch
salarySet.add(2.34);

salarySet.clear();
rS

When to Use Set


he

● Ordering is Not Important


● To Store the recordIds or Unique Elements like Emails, Phone
nt
Pa

Assignment
1. Create a List to Store Some of the Salesforce Certifications
a. Playaround with the methods
2. Create a Set to store the Salesforce Certifications related to Salesforce
Developers
a. Playaround with the methods

Link to Certificate -
https://trailhead.salesforce.com/en/credentials/administratoroverview/
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_collections.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_list.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_set.htm

DAY11 - Map Collection in Salesforce Part 1

ls
Recording Link
https://youtu.be/xWlbnqVPTpk

oo
Map in Salesforce

In Salesforce, a Map is a collection type that stores data in key-value pairs, where
ch
each key is unique and can be of any data type, and each value can also be of any
data type. Maps are useful when you want to store data in an organised way and
retrieve it based on a specific key.
rS
● With the help of Maps, we can store the value using Key-value pairs (KV)
● Map is the combination of Set & List where Key can not contain the
Duplicate Records & Value can contain a duplicate value.
● Map is very powerful while using Apex Code in Salesforce
he

Map<String, String> countryCurrencies = new Map<String, String>();


nt

Country (Key) United Japan India England France


States
Pa

Currency (Value) Dollars Yen Rupee Pound Euro

Object
String, Integer, Decimal, ApexClass, List, Map, Set
sObject
Standard Object
Custom Object

Structure of the Map Apex


In salesforce we can declare the Map in three different ways that are listed below.
Approach 1
Map<DataType, Datatype> nameOfMapVar; // null

Approach 2
Map<DataType, Datatype> nameOfMapVar = new Map<DataType, Datatype>();

Approach 3
Map<DataType, Datatype> nameOfMapVar = new Map<DataType, Datatype>{
'United States' => 'USD', 'India' => 'INR'

ls
};

oo
// Map<DataType, Datatype> nameOfMapVar; // null
// Map<DataType, Datatype> nameOfMapVar = new Map<DataType, Datatype>();
/* Map<DataType, Datatype> nameOfMapVar = new Map<DataType, Datatype>{
'United States' => 'USD', 'India' => 'INR'
};
*/
ch
Map<Integer, String> mothToValueMap = new Map<Integer, String>{
1 => 'April', 2 => 'May'
rS
};

Map<String, String> currencyMap; // null


// Approach 1
he

if(currencyMap == null){
//currencyMap = new Map<String, String>();
}
// Approach 2 with ??
nt

System.debug(currencyMap); // null
currencyMap = currencyMap ?? new Map<String, String>();
System.debug(currencyMap); //
// put
Pa

// get

Methods in Salesforce Map

● put(key, value) - Add a value in Map


● get(key) - get a value for the Key //String noOfEmployees =
accountMap.get(‘Acme’);
● containsKey(key) - accountMap.containsKey(‘Amit’); //True/False
public class CurrencyManager {
String name;
static String address;
static Integer age = 10;
{
System.debug(name);
System.debug('Instance block');
name = 'Amit Singh';
address = '123 Main Street';
}
static {

ls
System.debug(age);
address = '123 Main Street...';
System.debug('Static block');

oo
System.debug(address);
}
public CurrencyManager(){
System.debug('Constructor') ;
} ch
public static void getCurrency(Map<String, String> currencyMap){
System.debug(currencyMap);
rS
currencyMap = currencyMap ?? new Map<String, String>();
System.debug(currencyMap.size());
currencyMap.put('United States', 'USD');
currencyMap.put('India', 'INR');
he

currencyMap.put('Australia', 'AUD');
System.debug(currencyMap);
currencyMap.put('India', 'Rupee');
System.debug(currencyMap);
nt

// get method
// String currencyX = currencyMap.get('');
String currencyX = currencyMap.get('Bharat');
System.debug(currencyX);
Pa

String currency_x = currencyMap.get('India');


System.debug(currency_x);

// containsKey - Method
if(currencyMap.containsKey('Bharat')){
String currency_y = currencyMap.get('Bharat');
System.debug(currency_y);
// Business Logic
}else{
currencyMap.put('Bharat', 'INR');
}
System.debug(currencyMap);
currencyMap.clear();
}
}

For more information refer to this link -


https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_meth
ods_system_map.htm

Resources

● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/

ls
langCon_apex_collections.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex

oo
_methods_system_list.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_set.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
ch _methods_system_map.htm

Map<String, List<String> > countryStateMap = new Map<String, List<String> >();


rS
There are numerous methods of Map in Salesforce, however, there are some that
are being used widely by the developers.

● keySet() - Set<Key Data Type> // Set<String> accountNameSet =


accountMap.keySet();
he

● values() - List<Value Data Type>


○ List<String> accountEmployees = accountMap.values();
○ List<Object> accountEmployees = objectMap.values();
○ List< List<String> > statesList = countryStateMap.values();
nt

● clear()
● size()
● isEmpty()
Pa

● remove( key )

DAY12 - Map Collection in Salesforce Part 2


Recording
Link - https://youtu.be/xuVd2XYFVWg

Map<String, String> countryMap = new Map<String, String>();


countryMap.put('United States', 'USD');
countryMap.put('India', 'INR');
// Set of Keys - Set<String>
Set<String> keys = countryMap.keySet();
System.debug(keys);
// value(); ~= List of values - List<String>
List<String> values = countryMap.values();
System.debug(values);
Map<Integer, String> monthMap = new Map<Integer, String>();
// Set of Keys - Set<Integer>
Set<Integer> months = monthMap.keySet();
system.debug(months);
// value(); ~= List of values - List<String>
List<String> monthNames = monthMap.values();

ls
System.debug(monthNames);

oo
// India ( Country ) - All States - 'UP, MP, Rajasthan, ...'
// USA - All States
Map<String, List<String> > countryToStateMap = new Map<String,
ch
List<String> >();
List<String> IndiaStates = new List<String>();
IndiaStates.add('UP');
IndiaStates.add('MP');
rS
IndiaStates.add('Delhi');
IndiaStates.add('UK');
countryToStateMap.put('India', IndiaStates);
countryToStateMap.put('Australia',new List<String>{'NSW','VIC','QLD'});
he

// values() - List of values


List< List<String> > values = countryToStateMap.values();
System.debug(values);
nt

// Account and their cases


Map<Account, List<Case> > accountToCaseMap = new Map<Account, List<Case>
>();
Set<Account> accountSet = accountToCaseMap.keySet();
Pa

List< List<Case> > casesList = accountToCaseMap.values();


// Account Id and their cases
Map<Id, List<Case> > accountIdToCaseMap = new Map<Id, List<Case> >();

Map<String, List<String> > countryToStateMap = new Map<String,


List<String> >();
String country = 'India';
String stateName = 'UP';
// iteration #1
if(countryToStateMap.containsKey(country)){
} else {
countryToStateMap.put(country,new List<String>{stateName});
}

// iteration #2 - State is New Delhi


if(countryToStateMap.containsKey(country)){
// logic will be executed
// Step1 - get the value with the help of get Method
List<String> tempList = countryToStateMap.get(country);
tempList.add('New Delhi');

ls
countryToStateMap.put(country, tempList);
} else {
//countryToStateMap.put(country,new List<String>{stateName});

oo
}
countryToStateMap.put('Australia',new List<String>{'NSW','VIC','QLD'});
System.debug(countryToStateMap);

ch
// bundles of notes/dollars
// 10$, 5$, 10Rs, 5Rs
rS

Assignment

1. Create a List to Store Some of the Salesforce Certifications


2. Create a Set to store the Salesforce Certifications related to Salesforce
he

Developers
3. Create a Map to store the Salesforce Certifications in the following Fashion
- Note you can get the list of all the certifications by Job Role from here -
https://trailhead.salesforce.com/en/credentials/administratoroverview/
nt

a. Key - Developer - Value - List of All the Certifications


b. Key - Admin - Value - List of All the Certifications
c. Key - Architect - Value - List of All the Certifications
Pa

Resources

● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_collections.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_list.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_set.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_map.htm
Day13 - Looping Statement in Salesforce

Recording Link

https://youtu.be/nVBoB9VTFfg

Loops in Salesforce

Loops - There are mainly 3 types of Loops in Salesforce and the most
widely used loop is for loop. Below are the names of all the loops in
Salesforce.

ls
● For Loop
● While Loop
● Do While Loop

oo
Salesforce supports 3 different variants of for loops

1. The traditional loop


ch
for (Integer i=0; exit_condition; increment_stmt) {
code_block
rS
}

// Fastest For Loop


for (Integer i = 0; list.size()-1; i++) {
he

code_block
}
nt

/*for (Integer i=0; exit_condition; increment_stmt) {


code_block
}*/
Pa

List<String> fruits = new List<String>();


fruits.add('Banana');
fruits.add('Grapse');
fruits.add('Orange');

for(Integer i=0; i < fruits.size(); i++ ){


// logic
// get(index)
System.debug(i);
String furitName = fruits.get(i);
System.debug(furitName);
}
/*
fruits.size() = 3
#iteration 1
i = 0;
0 < 2 == True;
fruits.get(0) - Banana
#iteration 2
i = i + 1 = 1;
1 < 2 = True
fruits.get(1) - Grapse

ls
#iteration 3
i = i +1 = 1 + 1 = 2
i < size = 2 < 3 = True

oo
fruits.get(2) - Orange
#iteration 4
i = i +1 = 2 + 1 = 3
i < size = 3 < 3 = False
*/ ch
2. The list or set iteration for the loop
rS

for (sObject/String/Integer: list/set) {


code_block
}
he

/*for (sObject/String/Integer: list/set) {


code_block
nt

}*/
List<String> fruits = new List<String>();
fruits.add('Banana');
Pa

fruits.add('Grapse');
fruits.add('Orange');

for(String fruitName : fruits ){


System.debug(fruitName);
}

List<Account> accountList = new List<Account>();


accountList.add( new Account() );
accountList.add( new Account() );
accountList.add( new Account() );
accountList.add( new Account() );
accountList.add( new Account() );
for(Account acc : accountList){
acc.Name = String.valueOf( Math.random() );
System.debug(acc);
}

3. The SOQL for loop

for (sObject : [soql_query]) {

ls
code_block
}

oo
OR

ch
for (list<variable> : [soql_query]) {
code_block
}
List<String> fruits = new List<String>{'Apple', 'Banana', 'Mango'};
rS
/*
for (Integer i=0; exit_condition; increment_stmt) {
code_block
}
he

i++ => i = i+1


*/
Integer length = fruits.size();
nt

While Loop
Pa

while(condition){ // True/False
// code block statements
}

List<String> fruits = new List<String>{'Apple', 'Banana', 'Mango'};


Integer i = 0;
while(i < fruits.size() ){
System.debug(' '+fruits.get(i) );
i++;
}

List<String> fruits = new List<String>{'Apple', 'Banana', 'Mango'};


Integer i = 0;
while(i < fruits.size() ){
System.debug(' '+fruits.get(i) );
i++;
}
// DateTime, System --> now(); - getTime()
System.debug('Before '+System.now());
Integer j = 0;

ls
while(j < 2000 ){
System.debug(' '+j );
j++;

oo
}
System.debug('After '+System.now());

ch
rS
he
nt
Pa

List<String> fruits = new List<String>{'Apple', 'Banana', 'Mango'};


Integer i = 0;
while(i < 0 ){
System.debug(' '+fruits.get(i) );
i++;
}

do {
System.debug(' '+fruits.get(i) );
} while( i < 0 );

Map<String, String> accountMap = new Map<String, String>();


accountMap.put('Acme', '3454');

ls
accountMap.put('XYZ Corp', '5000');
accountMap.put('ABC Company', '7500');

oo
// keySet()
// values()
//Set<String> keysSet = accountMap.keyset();
ch
for(String key: accountMap.keyset()){
System.debug(key);
String value = accountMap.get(key);
System.debug(value);
rS
}

Assignment

1. Create a List to Store Some of the Salesforce Certifications


he

2. Create a Set to store the Salesforce Certifications related to Salesforce


Developers
3. Create a Map to store the Salesforce Certifications in the following Fashion
- Note you can get the list of all the certifications by Job Role from here -
nt

https://trailhead.salesforce.com/en/credentials/administratoroverview/
a. Key - Developer - Value - List of All the Certifications
b. Key - Admin - Value - List of All the Certifications
Pa

c. Key - Architect - Value - List of All the Certifications


4. Use the collections from the previous exercise and iterate those using For
Loop and Do while Loop
5. Print the Information using Debug Log

Resources

● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
langCon_apex_collections.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_list.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_set.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_map.htm

DAY14 - Getter/Setter in Salesforce


Recording Link

ls
https://youtu.be/lG0A4F8UbrM

Definition

oo
Getters and setters are methods used to access and update the values of class
properties, also known as instance variables or fields.

ch
Getters are used to retrieve the value of a field, while setters are used to set or
update the value of a field.

public String getFirstName() {


rS
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
he

The code snippet public String name { get; private set; } defines a property called
"name" with a public getter method and a private setter method.
nt

The get keyword specifies that the "name" property can be accessed publicly,
meaning that other classes can read the value of this property using the getter
Pa

method.

The private set keyword specifies that the "name" property can only be set or
modified from within the same class. Other classes cannot directly modify the
value of this property, which helps to enforce data encapsulation and prevent
unintended changes to the property's value.

By using this syntax, developers can define read-only properties that can be
accessed publicly but cannot be modified outside of the class. This is useful for
exposing data to other classes or components while maintaining control over how
that data is updated.
public class Animal {
String name = 'Max';
Integer age = 3;
public String color {
public get {
// SOQL Query
String accountName = [SELECT Name FROM Account LIMIT 1].Name;
// DML
/*Account acc = new Account(
Name = 'Salesforce.com'
);
insert acc;*/

ls
return accountName;
}

oo
public set {
// SOQL
// DML
System.debug(value);

}
}
ch
color = value;
rS
public void setColor(String color){
this.color = color;
}

public void setAnimalName(String animalName){


he

this.name = animalName;
}
public String getAnimalName(){
return this.name;
nt

public void setAge(Integer age){


Pa

this.age = age;
}
public Integer getAge(){
return this.age;
}
}

Call by Reference & Call by Value


Call by Value in Apex
Definition:
In Call by Value, a copy of the actual parameter’s value is passed to the
method. This means that any changes made to the parameter inside the
method do not affect the original variable outside the method.

Characteristics:

● Primitives: Primitive data types (e.g., Integer, Double, Boolean, String,


Date, etc.) are passed by value.

ls
● Copy of Value: A separate copy of the value is created for the method
parameter.
● No Side-Effects: Changes to the parameter within the method do not

oo
affect the original variable.

public class CallByValueExample {


ch
public static void modifyValue(Integer x) {
x = 100; // This change will not affect the original
variable
rS
}
public static void main() {
Integer a = 10;
modifyValue(a);
System.debug('Value of a: ' + a); // Output: Value of a:
he

10
}
}
nt

Call by Reference in Apex


Pa

Definition:
In Call by Reference, a reference to the actual parameter is passed to the method.
This means that changes made to the parameter inside the method can affect the
original variable outside the method.

Characteristics:
● Objects: Non-primitive data types (e.g., custom objects, List, Map, Set, and
standard Salesforce objects like Account, Contact, etc.) are passed by
reference.
● Reference to Object: A reference to the original object is passed, allowing
methods to modify the actual object.
● Side-Effects: Changes to the object within the method affect the original
object.

public class CallByDemo {


public static void add(Integer a, Integer b){
a = 3;

ls
b = 5;
System.debug(a);
System.debug(b);

oo
Integer c = a + b;
/*
Integer a = 10;
Integer b = 10;
ch CallByDemo.add(a, b);
System.debug(a);
System.debug(b);
*/
rS
}

public static void add(List<String> fruits){


System.debug(fruits);
he

fruits.add('Apple');
fruits.add('Banana');
}
}
nt
Pa

DAY15 - Introduction to SOQL in Salesforce


Recording Link
https://youtu.be/ug3Wv6PWK94
Definition
SOQL (Salesforce Object Query Language) is a query language that searches for
and retrieves data from Salesforce objects. SOQL is similar to SQL, but it is
specifically designed for the Salesforce platform and supports features such as
relationship queries and custom object searches.

Person

1. Name
2. Age

ls
3. Relationship

oo
Syntax

SELECT FieldAPIName1, FieldAPIName2 FROM


Object_ApiName__c(Account/Contact/Payment__c)
ch
SELECT one or more fields
FROM an object
rS
WHERE filter statements
results are ordered (optional)
Here are some key concepts and examples of SOQL in Salesforce:
he

● WHERE
○ Single Filter - ( Name = ‘United Oil’)
○ Multiple Values Filter - Name IN (‘United Oil’, ‘Acme’)
nt

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry FROM Account


Pa

SELECT Account.Name, Account.AccountNumber, Account.Fax, Account.Phone,


Account.Rating, Account.Industry FROM Account

SELECT Account.Name, Account.AccountNumber, Account.Fax, Account.Phone,


Account.Rating, Account.Industry, Account.Active__c FROM Account

SELECT acc.Name, acc.AccountNumber, acc.Fax, acc.Phone, acc.Rating,


acc.Industry, acc.Active__c FROM Account acc

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry FROM Account


WHERE Industry IN('Education', 'Energy', 'Construction')
SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue
FROM Account WHERE Industry = 'Education' OR Industry = 'Energy' OR Industry
= 'Construction'

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


FROM Account WHERE (Industry = 'Education' OR Industry = 'Energy' OR Industry
= 'Construction') AND ( AnnualRevenue > 30000 )

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


FROM Account WHERE (Industry IN('Education', 'Energy', 'Construction') ) AND (
AnnualRevenue > 30000 )

ls
SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue

oo
FROM Account WHERE (Industry IN('Education', 'Energy', 'Construction') ) AND (
AnnualRevenue > 30000 )

SELECT Id, Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue,


ch
CreatedDate FROM Account WHERE CreatedDate = LAST_N_DAYS:30

Assignment
rS

● List All the Contact Records


● List All the Contact Records where the Email is not blank
● List All the Contact Records Where the Email is Blank
● List All the Contact Records Where the Title contains the developer
he

● List All the Contact Records Where the Title Starts with the Manager
● List All the Opportunity Where the Amount is more than 29000
● List All the Opportunity count state wise
● List All the Opportunity Amount Stage Wise
nt

● List All the Open & Closed Opportunities

Resources
Pa

● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select_agg_functions.htm
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select.htm
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select_examples.htm
● Date Literrals -
https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select_dateformats.htm
DAY16 - SOQL Continue
Recording Link

https://youtu.be/TKyhzkJbXMU

SOQL Continue

● WHERE
○ Single Filter - ( Name = ‘United Oil’)
○ Multiple Values Filter - Name IN (‘United Oil’, ‘Acme’)

ls
○ Like Filter - Name Like ‘%United Oil’
■ %abc% - Keyword is found anywhere in the name
■ %abc - The starting char could be anything but the ending will

oo
always be abc
■ abc% - Starring will always be ABC and the ending will be
anything
○ Not Like Filter
ch ■ Not Name Like ‘TEST%’
○ CreatedDate > 2021-09-12T22:16:30.000Z
○ LAST_N_DAYS
rS
■ Includes current Day
○ NEXT_N_DAYS
■ Includes current Day
○ NEXT_N_WEEKS
○ LAST_N_MONTHS
he

■ Not Includes current Day


● ORDER BY
○ ORDER BY Name DESC
○ ORDER BY Name ASC
nt

● LIMIT
○ LIMIT 200
Pa

SOQL Structure

Here is how the SOQL will be constructed with various Keywords and parameters
ls
oo
ch
Queries we developed in the class
rS
SELECT Id, AccountId, LastName, FirstName, Phone, Fax, Email, Title,
Department, Level__c, Languages__c FROM Contact WHERE CreatedDate >
2024-01-01T00:00:00Z
he

SELECT Id, AccountId, LastName, FirstName, Phone, Fax, Email, Title,


Department, Level__c, Languages__c FROM Contact

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


nt

FROM Account ORDER BY AnnualRevenue DESC NULLS LAST, Industry DESC NULLS
LAST

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


Pa

FROM Account ORDER BY AnnualRevenue ASC NULLS LAST, Industry DESC NULLS
LAST

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


FROM Account ORDER BY AnnualRevenue ASC NULLS LAST

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


FROM Account ORDER BY AnnualRevenue DESC NULLS LAST

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


FROM Account ORDER BY AnnualRevenue DESC
SELECT Name, AccountNumber, Fax, Phone, Rating, Industry__c FROM Account
WHERE Industry__c includes ('Apparel;Banking', 'Banking', 'Apparel')

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry__c FROM Account


WHERE Industry__c != 'Agriculture;Apparel;Banking'

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry__c FROM Account


WHERE Industry__c = 'Agriculture;Apparel;Banking'

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry__c FROM Account


WHERE Industry__c = 'Apparel;Banking;'

ls
SELECT Name, AccountNumber, Fax, Phone, Rating, Industry__c FROM Account
WHERE Industry__c = 'Apparel;Banking'

oo
SELECT Name, AccountNumber, Fax, Phone, Rating, Industry__c FROM Account
WHERE Industry__c includes ('Apparel;Banking')

ch
SELECT Name, AccountNumber, Fax, Phone, Rating, Industry__c FROM Account
WHERE Industry__c includes ('Banking')

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue,


rS
Industry__c FROM Account WHERE Industry__c includes ('Banking')

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue,


Industry__c FROM Account WHERE Industry__c includes ('Education')
he

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue,


Industry__c FROM Account
nt

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


FROM Account WHERE NOT Name LIKE '%oil%'

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


Pa

FROM Account WHERE Name LIKE 'Salesforce'

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


FROM Account WHERE Name LIKE 'jayo'

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


FROM Account WHERE Name LIKE 'edge%'

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


FROM Account WHERE Name LIKE '%oil'

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


FROM Account WHERE Name LIKE '%oil%'

SELECT Name, AccountNumber, Fax, Phone, Rating, Industry, AnnualRevenue


FROM Account

DAY17 - Aggregate SOQL Queries in Salesforce

Recording Link

ls
https://youtu.be/k1nlDEwAEZI

oo
Group BY in Salesforce

● COUNT()
○ COUNT()
ch
○ COUNT(ID)
○ COUNT(AnnualRevenue)
■ 117 records
■ 110 - Annual Revenue Populated
rS
● GROUP BY - Aggregate Query
○ Aggregate Methods
■ count
■ sum
he

■ Integer
■ Number
■ Currency
■ min
nt

■ Integer
■ Number
■ Currency
■ Date
Pa

■ DateTime
■ max
■ Integer
■ Number
■ Currency
■ Date
■ DateTime
○ GROUP BY Name
○ GROUP BY ROLLUP(LeadSource)
○ GROUP BY ROLLUP(Status, LeadSource)
○ GROUP BY CUBE(Type, BillingCountry)
Having clause in Salesforce

In Salesforce, the HAVING clause is used in SOQL (Salesforce Object Query


Language) to filter the results of an aggregate query. It is similar to the
WHERE clause but is applied to the results of an aggregate function.

When to Use the HAVING Clause


The HAVING clause is used in SOQL queries that include GROUP BY to filter
groups of records returned by aggregate functions like COUNT(), SUM(),
AVG(), MIN(), and MAX().

ls
Syntax
The general syntax for using the HAVING clause in a SOQL query is:

oo
SELECT field1, aggregateFunction(field2)
FROM object
WHERE condition
ch
GROUP BY field1
HAVING aggregateFunction(field2) condition
rS

Example
Let's consider an example where we have an Opportunity object, and we
want to find all accounts that have more than one closed opportunity.
he

SELECT AccountId, COUNT(Id)


FROM Opportunity
WHERE StageName = 'Closed Won'
nt

GROUP BY AccountId
HAVING COUNT(Id) > 1
Pa
ls
oo
ch
Query used in the Class
rS

Select Id FROM Account

Select count() FROM Account


he

Select count() FROM Account WHERE AnnualRevenue != null

Select count(AnnualRevenue) FROM Account


nt

Select min(AnnualRevenue) FROM Account

Select max(AnnualRevenue) FROM Account


Pa

Select sum(AnnualRevenue) FROM Account

Select avg(AnnualRevenue) FROM Account

Select min(CloseDate) FROM Opportunity

Select min(CreatedDate) FROM Opportunity

Select Id, AnnualRevenue, Industry FROM Account

Select sum(AnnualRevenue) FROM Account GROUP BY Industry


Select sum(AnnualRevenue), Industry FROM Account GROUP BY Industry

Select sum(AnnualRevenue), Industry, count(Name) FROM Account GROUP BY


Industry

Select sum(AnnualRevenue), Industry, Type, count(Name) FROM Account


GROUP BY Industry, Type

Select sum(AnnualRevenue), Industry, Type, count(Name) FROM Account


GROUP BY Industry, Type ORDER BY Industry DESC NULLS LAST

ls
Select count(Id), AccountId FROM Contact Group By AccountId

oo
Select count(Id) totalContact, AccountId FROM Contact Group By AccountId

Select count(Id) totalContact, AccountId parentId FROM Contact Group By


AccountId
ch
Select count(Id) totalContact, AccountId parentId FROM Contact

Select count(Id) totalContact, AccountId parentId, Gender__c FROM


rS
Contact Group By AccountId, Gender__c

Select max(AnnualRevenue) FROM Account Where AnnualRevenue < 908934


he

Select count(Id) FROM Contact

Select count(Id) FROM Contact Group By AccountId


nt

Select count(Id), AccountId FROM Contact Group By AccountId Having


count(Id) > 1

Select sum(Amount), AccountId FROM Opportunity Group By AccountId Having


Pa

sum(Amount) > 10000

SELECT count(Id), AccountId, Email FROM Contact GROUP By AccountId,


Email

SELECT count(Id), AccountId, Email FROM Contact GROUP By AccountId,


Email HAVING count(Id) > 1

SELECT count(Id), AccountId, Email FROM Contact WHERE AccountId IN


(Select Id FROM Account WHERE Id='001dL000001rKZZQA2')

SELECT Id, AccountId, Email FROM Contact WHERE AccountId IN (Select Id


FROM Account WHERE Id='001dL000001rKZZQA2')

Assignment

● List All the Contact Records


● List All the Contact Records where the Email is not blank
● List All the Contact Records Where the Email is Blank
● List All the Contact Records Where the Title contains the developer
● List All the Contact Records Where the Title Starts with the Manager
● List All the Opportunity Where the Amount is more than 29000
● List All the Opportunity count state wise

ls
● List All the Opportunity Amount Stage Wise
● List All the Open & Closed Opportunities

oo
Resources

● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select_agg_functions.htm
ch
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select.htm
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
rS
sforce_api_calls_soql_select_examples.htm

DAY18 - Relationship Query in Salesforce SOQL


he

Recording Link
https://youtu.be/6Yrsrg6o8hA

OFFSET in SOQL Query


nt

SELECT Max(Amount) FROM Opportunity


Pa

SELECT Id, Amount FROM Opportunity

SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST

SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
1

SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
2

SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
1 OFFSET 1
SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
1 OFFSET 6

SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
1 OFFSET 5

SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT
1 OFFSET 4

SELECT Id, Amount FROM Opportunity ORDER BY Amount DESC NULLS LAST LIMIT

ls
1 OFFSET 0

oo
Relationship Query in Salesforce

ch
rS
he
nt
Pa

Child to Parent SOQL Query

SELECT Id, Name, Email, Phone FROM Contact

SELECT Id, Name, Email, Phone, Account.Name, Account.Id,


Account.Phone, Account.Industry, Account.Rating, Account.Active__c
FROM Contact
SELECT Id, Name, Email, Phone, Account.Name, Account.Id,
Account.Phone, Account.Industry, Account.Rating,
Account.Active__c, Primary_Contact__r.Name FROM Contact
SELECT Id, Name, Email, Phone, Account.Name, Account.Id FROM
Contact

SELECT Id, Name, Email, Phone, Account.Name, AccountId FROM


Contact

SELECT Id, Name, Email, Phone, Account.Name, AccountId,

ls
Primary_Contact__c FROM Contact

oo
SELECT Id, Name, Email, Phone, Account.Name, Account.Parent.Name
FROM Contact

SELECT Id, Name, Email, Phone, Account.Name, Account.Parent.Name,


ch
Account.Parent.Parent.Name FROM Contact

SELECT Id, Account.Name, Account.Parent.Name,


rS
Account.Parent.Parent.Name FROM Contact

SELECT Id, Account.Name, Account.Parent.Name,


Account.Parent.Parent.Name, Account.Parent.Parent.Parent.Name,
he

Account.Parent.Parent.Parent.Parent.Name FROM Contact

SELECT Id, Account.Name, Primary_Contact__c FROM Contact


nt

SELECT Id, Account.Name, Primary_Contact__c,


Primary_Contact__r.Name, Primary_Contact__r.Account.Name FROM
Contact
Pa

Parent to Child SOQL Query

SELECT Id, Name FROM Account

SELECT Id, Name, (SELECT Id, Name, Email FROM Contacts) FROM Account

SELECT Id, Name, (SELECT Id, Name, Email FROM Contacts), (SELECT Id,
CaseNumber FROM Cases) FROM Account
SELECT Id, Name, (SELECT Id, Name, Email FROM Contacts WHERE Email <>
null), (SELECT Id, CaseNumber FROM Cases WHERE IsClosed=False) FROM
Account

SELECT Id, Name, (SELECT Id, Name, Email FROM Contacts WHERE Email <>
null), (SELECT Id, CaseNumber FROM Cases WHERE IsClosed=False) FROM
Account WHERE Industry = 'Education'

SELECT Id, Name, (SELECT Id, Name, Email FROM Contacts WHERE Email <>
null), (SELECT Id, CaseNumber FROM Cases WHERE IsClosed=False), (SELECT
Id, Name, Account__c FROM Invoice__r) FROM Account

ls
SELECT Id, Name, (SELECT Id, Name, Email, (SELECT Id, CaseNumber FROM
Cases) FROM Contacts WHERE Email <> null), (SELECT Id, CaseNumber FROM

oo
Cases WHERE IsClosed=False), (SELECT Id, Name, Account__c FROM
Invoice__r) FROM Account

SELECT Id, Name, (SELECT Id, Name, Email, (SELECT Id, CaseNumber FROM
ch
Cases) FROM Contacts WHERE Email <> null) FROM Account

SELECT Id, Name, (SELECT Id, Name, Email, (SELECT Id, CaseNumber FROM
Cases) FROM Contacts WHERE Email <> null) FROM Account
rS

Assignment
he

● List All the Contact Records with Account Name, Rating, and Industry
● Query on the Account object and also query all the related Contact, Case,
Order, and Opportunities
● Create 3 Custom Objects
nt

○ An invoice that is related to the Account


○ An Invoice Line Item object that is related to the Invoice
○ A payment that is related to the Account
Pa

○ Now Query on Invoice Records to Query the Related Account


Information and Also all the related Invoice Line Items
○ Note: - Please create the required records for these objects
● Query All the Contact Record that does not have any Account Related To
● Query All the Contact Records that are related to Account Record
● Query All the Account Record that does not have any contact
● Query All the Account Record that does have min one Contact Record
● Query the Invoice Record, its Related Account, and Account Related Parent
Account Record
● Query the Invoice Line Item Record, its Related Invoice, Invoice Related
Account, and Account Related Parent Account Record
● Write a SOQL Query to Show the MAX amount from the opportunity
● Write a SOQL Query to identify the duplicate records by Name & Email
● Write a SOQL Query to display the count of Accounts for every Industry

Resources

● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select_agg_functions.htm
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/

ls
sforce_api_calls_soql_select.htm
● https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/
sforce_api_calls_soql_select_examples.htm

oo
DAY19 - Polymorphic query
ch
Recording Link

https://youtu.be/w_4PUHgA-Pc
rS
Polymorphic query

In Salesforce, a polymorphic relationship refers to a type of relationship between


objects where the referenced object can be one of several different types.
he

For instance, in a Task object, the "Who (Name)" relationship field can be
associated with either a Contact or a Lead, allowing for greater flexibility in data
modelling.
nt

Who refers To - Person - Lead/Contact

In a Task object, the “What (Related To)” relationship can be related to any custom
Pa

or standard object except Lead or Contact

Structure

SELECT Id, Subject, Description, Status,


TYPEOF What
WHEN
Account
THEN
Id, Name, Industry, Phone
WHEN
Opportunity
THEN
Id, Name, Amount, StageName
WHEN
CASE
THEN
Id, CaseNumber, Status, ContactId
END
FROM
Event
WHERE
What.TYPE IN ('Account', 'Opportunity' )

ls
Example #1

oo
SELECT Id, Subject,
TYPEOF What
WHEN Account THEN Phone, NumberOfEmployees
ch
WHEN Opportunity THEN Amount, CloseDate
ELSE Name, Email
END
FROM Event
rS
WHERE What.Type IN ('Account', 'Opportunity')
Example #2
SELECT Id, CaseNumber, Subject,
TYPEOF OWNER
he

WHEN
User
THEN
Id, Name, Email
nt

WHEN
Group
THEN
Pa

NAME
END
FROM
Case
WHERE
OWNER.TYPE IN('User', 'Queue' )

Example Apex

List<Event> eventList = [SELECT Id, Subject, Description,


TYPEOF What
WHEN
Account
THEN
Phone, Name, Industry
WHEN
Opportunity
THEN
Id, Name, Amount, CloseDate
END
FROM
Event
WHERE What.TYPE IN ('Account', 'Opportunity' )];

ls
System.debug(' eventList + \n '+ JSON.serialize( eventList ) );

oo
for (Event evt: eventList) {
if (evt.WHAT instanceof Account) {
Account acc = evt.WHAT;
System.debug(' acc '+acc);
ch
} else if (evt.What instanceof Opportunity) {
Opportunity opp = evt.WHAT;
System.debug(' acc '+opp);
}
rS
}

Query used in Demo


he

SELECT Id, WhoId, WhatId, Subject, ActivityDate, Status, Priority,


Description FROM Task
nt

SELECT Id, Who.Name, Who.Email, WhatId, Subject, ActivityDate, Status,


Priority, Description FROM Task

SELECT Id, TYPEOF WHO WHEN Contact THEN Id, Name, Email, AccountId WHEN
Pa

LEAD THEN Id, Name, Company, AnnualRevenue END, WhatId, Subject,


ActivityDate, Status, Priority, Description FROM Task

SELECT Id, WhoId, Who.Type, WhatId, What.Type, Subject, ActivityDate,


Status, Priority, Description FROM Task

SELECT Id, WhoId, Who.Type, WhatId, What.Type, Subject, ActivityDate,


Status, Priority, Description FROM Task WHERE What.Type = 'Contact'

SELECT Id, WhoId, Who.Type, WhatId, What.Type, Subject, ActivityDate,


Status, Priority, Description FROM Task WHERE Who.Type = 'Contact'
SELECT Id, TYPEOF WHO WHEN Contact THEN Id, Name, Email, AccountId WHEN
LEAD THEN Id, Name, Company, AnnualRevenue END, WhatId, Subject,
ActivityDate, Status, Priority, Description FROM Task WHERE Who.Type IN
('Contact', 'Lead')

SELECT Id, TYPEOF WHO WHEN Contact THEN Id, Name, Email, AccountId WHEN
LEAD THEN Id, Name, Company, AnnualRevenue END, WhatId, Subject,
ActivityDate, Status, Priority, Description, TYPEOF What WHEN Account
THEN Id, Name, Rating WHEN Opportunity THEN Id, Name, StageName,
CloseDate END FROM Task WHERE Who.Type IN ('Contact', 'Lead') OR
What.Type IN ('Account', 'Opportunity' )

ls
oo
SOQL Query in Apex Class

public class SOQLDemo {


ch
public static void polymorphic(){

List<Task> taskList = [SELECT Id, TYPEOF WHO


rS
WHEN
Contact
THEN
Id, Name, Email, AccountId
he

WHEN
LEAD
THEN
Id, Name, Company, AnnualRevenue
nt

END,
WhatId, Subject, ActivityDate, Status, Priority, Description,
TYPEOF What
WHEN
Pa

Account
THEN
Id, Name, Rating
WHEN
Opportunity
THEN
Id, Name, StageName, CloseDate
END
FROM
Task
WHERE
Who.Type IN ('Contact','Lead')
OR What.Type IN ('Account', 'Opportunity' )
];
for(Task t: taskList){
if(t.What instanceof Account){
Account acc = t.What;
}
if(t.Who instanceof Contact){
Contact con = t.Who;
System.debug(con);
}
if(t.Who instanceof Lead){
Lead l = t.Who;

ls
System.debug(l);
}

oo
}
}

public static void aggregate(){


ch
List<AggregateResult> agResult = [SELECT count(Id), AccountId FROM
Contact Group BY AccountId];
}
rS
public static void query(List<Id> accountIdsSet){ // collections for the filters
List<Account> accountList = [SELECT Id, Name FROM Account WHERE Id =:
accountIdsSet ];
for(Account acc: accountList){
System.debug(acc.Name);
he

// business
}
}
nt

public static void query(String industry){


List<Account> accountList = [SELECT Id, Name FROM Account WHERE
Industry =: industry ];
Pa

// >=:
// <=:
// >:
// <:
System.debug(accountList);
List<Opportunity> opptyList = [SELECT Id, Name, Amount, CloseDate FROM
Opportunity WHERE Name = 'ABVG' LIMIT 1];
System.debug(opptyList); // blank list
if(!opptyList.isEmpty()){
// do logic here
}
List<Case> caseList = new List<Case>([SELECT Id, CaseNumber FROM
Case]);
}
}

Extra Hands-On

SELECT count(Id), Industry FROm Account Group By Industry

ls
SELECT Id, Name, Email, Company, OwnerId, TYPEOF OWNER WHEN User THEN
Id, Name, Email WHEN Group THEN Id, Name END FROM Lead WHERE
Owner.Type IN ('User', 'Group')

oo
SELECT Id, Subject, Description, WhatId, TYPEOF What WHEN Account THEN Id,
Name, Industry, Phone WHEN Opportunity THEN Id, Name, Amount, StageName
ch
WHEN CASE THEN Id, CaseNumber, Status, ContactId END FROM Event WHERE
What.TYPE IN ('Account', 'Opportunity' )
rS
SELECT Id, Subject, Description, WhatId, TYPEOF What WHEN Account THEN Id,
Name, Industry, Phone WHEN Opportunity THEN Id, Name, Amount, StageName
WHEN CASE THEN Id, CaseNumber, Status, ContactId END FROM Event WHERE
What.TYPE IN ('Account', 'Opportunity', 'Case' )
he

SELECT Id, Subject, Description, WhatId, TYPEOF What WHEN Account THEN Id,
Name, Industry, Phone WHEN Opportunity THEN Id, Name, Amount, StageName
WHEN CASE THEN Id, CaseNumber, Status, ContactId END FROM Event
nt

SELECT Id, Subject, Description, TYPEOF What WHEN Account THEN Id, Name,
Industry, Phone WHEN Opportunity THEN Id, Name, Amount, StageName WHEN
Pa

CASE THEN Id, CaseNumber, Status, ContactId END FROM Event

SELECT Id, WhoId, WhatId, Subject, ActivityDateTime, ActivityDate, Description,


What.Id, What.Name FROM Event

SELECT Id, WhoId, WhatId, Subject, ActivityDateTime, ActivityDate, Description,


What.Id, What.Name FROM Event Where What.Type = 'Account'

SELECT Id, WhoId, WhatId, Subject, ActivityDateTime, ActivityDate, Description,


What.Type FROM Event Where What.Type = 'Account'
SELECT Id, WhoId, WhatId, Subject, ActivityDateTime, ActivityDate, Description,
What.Type FROM Event

SELECT Id, WhoId, WhatId, Subject, ActivityDateTime, ActivityDate, Description


FROM Event

SELECT Id, CaseNumber, Subject, TYPEOF OWNER WHEN User THEN Id, Name,
Email WHEN Group THEN NAME END FROM Case WHERE OWNER.TYPE IN('User',
'Queue' )

ls
SELECT Id, CaseNumber, Subject, TYPEOF OWNER WHEN User THEN Id, Name,

oo
Email WHEN Group THEN NAME END FROM Case

// 0 , 1, 10, 29
ch
String accountName = 'Genepoint';
List<Account> accountList = [SELECT Id, Name
FROM Account
WHERE Name =: accountName
rS
LIMIT 50000];
System.debug(' accountList '+ accountList);

List<Payment__c> paymentList = [SELECT Id, Name FROM Payment__c LIMIT


50000];
he

System.debug(' paymentList '+ paymentList);

if( paymentList.size() > 0){


nt

if( !paymentList.isEmpty() ){
Pa

List<String> accountIndustries = new List<String>();


accountIndustries.add('Education');
accountIndustries.add('Banking');
accountIndustries.add('Consulting');

List<Account> accountListWithIndustry = [SELECT Id, Name


FROM Account
WHERE Industry IN: accountIndustries
LIMIT 50000];
System.debug(' accountListWithIndustry '+ accountListWithIndustry);

// Single Value =:
// Multiple Values IN:

String keyword = 'test'; //% %


// %test%
String likeParams = '%'+keyword+'%'; // %test%

List<Account> accountListWithLike = [SELECT Id, Name

ls
FROM Account
WHERE Name Like:likeParams
LIMIT 50000];

oo
System.debug(' accountListWithLike '+ accountListWithLike);

public static void searchAccount(String name){


ch
String likeParams = '%'+name+'%'; // %test%
List<Account> accountListWithLike = [SELECT Id, Name
FROM Account
rS
WHERE Name Like:likeParams
LIMIT 50000];
System.debug(' accountListWithLike '+ accountListWithLike);
}
he

List<Event> eventList = [SELECT Id, Subject, Description, WhatId,


TYPEOF What
WHEN Account THEN Id, Name, Industry, Phone
nt

WHEN Opportunity THEN Id, Name, Amount, StageName


WHEN CASE THEN Id, CaseNumber, Status, ContactId END
FROM Event
WHERE What.TYPE IN ('Account', 'Opportunity', 'Case' )
Pa

];
System.debug(' eventList '+ eventList);

for(Event evt : eventList){


System.debug(' Event Record \n '+ evt);
System.debug(' Event Record \n '+ evt.Subject);
}

List<AggregateResult> agr = [SELECT count(Id), Industry


FROM Account Group By Industry];
System.debug(' agr '+ agr);
//SOQL For Loop
// Contact --> Account (AccountId)
Set<Id> accountIds = new Set<Id>();
for(Contact con : [SELECT Id, Name, AccountId FROM Contact Where Email <>
null LIMIT 50000]){
//System.debug(' contact records \n '+con);
accountIds.add(con.AccountId);
}
System.debug(' accountIds \n '+accountIds);
List<Account> limitedAccounts = [SELECT Id FROM Account WHERE Id IN :
accountIds ];

ls
oo
ch
rS
he

Assignment
nt

● Create an Apex Class and name is SOQLDemo


○ Create a method and name it searchAccount that accepts a single
parameter as the account name or portion of the account. Make a
Pa

SOQL Query to get all the accounts whose names are the matching
name as parameters and put a debug log.
○ Create a method polymorphicQueryDemo and make a SOQL query to
get the Event Information that is related to Account, Contact,
Opportunity, and Case. See more for the query
■ When What is Account SELECT Id, Name, Phone, Industry
■ When What is Opportunity SELECT Id, Name, Amount
■ When What is Case SELECT Id, CaseNumber, Subject, Status
■ When What is Contact SELECT Id, Name, Email, Phone
○ create a method soqlForLoopDemo and do the query on Account,
Contact, and Opportunity using SOQL For Loop
○ Create a method that accepts the List of Industries and the keyword.
Inside the method make the SOQL Query that filters the account
based on Industry and the name based on the parameter.
■ Note: - Name should be having “Like” the filter on the Account
Name field
○ Create a method and have the below query inside that method
■ Query All the Contact Record that does not have any Account
Related To
■ Query All the Contact Records that are related to Account
Record
■ Query All the Account Record that does not have any contact
■ Query All the Account Record that does have min one Contact

ls
Record
■ Query the Invoice Record, its Related Account, and Account
Related Parent Account Record

oo
■ Query the Invoice Line Item Record, its Related Invoice, Invoice
Related Account, and Account Related Parent Account Record
■ Write a SOQL Query to Show the MAX amount from the
ch opportunity
■ Write a SOQL Query to identify the duplicate records by Name
& Email
■ Write a SOQL Query to display the count of Accounts for every
rS
Industry

Resources

● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
he

langCon_apex_SOQL_polymorphic_relationships.htm
nt

DAY20 - SOQL Continue

Recording Link
Pa

https://youtu.be/GwZNedLx0Q0

Code used in the Live Class

public class SOQLDemo {

// UNABLE_LOCK_ROW
// 003dL000000GLS7QAO - Trigger - Still in Progress
// Trigger - 001dL000001rKZeQAM/003dL000000GLS7QAO
/**
* Use Order BY Clause
* Contact Record - SELECT Id FROM Contact Order BY AccountId
* FOR VIEW | REFERENCE | UPDATE
*/
public static void UNABLE_LOCK_ROW(){
List<Contact> conList = [SELECT Id, AccountId, Email FROM
Contact FOR UPDATE];
for(Contact con: conList){
// process is going on
}

ls
List<Account> accList = [SELECT Id, Name, Rating FROM Account
FOR UPDATE];
}

oo
public static void searchRecords(String keyword){
// keyword = oil
// contains %oil%
// %abc%
ch
// %xyz%
String likeParam = '%'+keyword+'%';
List<Account> accList = [SELECT Id, Name, Rating FROM Account
WHERE Name Like: likeParam];
rS
//System.debug(System.JSON.serializePretty(accList));
/** SOQL For Loop **/
Set<Id> accountIdsSet = new Set<Id>();
// 2K Records
he

// 2K/200 - 5
for(Account acc: [SELECT Id, Name, Rating FROM Account WHERE
Name Like: likeParam] ){
accountIdsSet.add(acc.Id);
nt

System.debug(acc.Id);
}
/** SOQL For Loop **/
Integer count = 0;
Pa

for(List<Account> accList1: [SELECT Id FROM Account] ){


System.debug(accList1.size());
count += 1;
}
System.debug(count);
}

public static void polymorphic(){

List<Task> taskList = [SELECT Id, TYPEOF WHO


WHEN
Contact
THEN
Id, Name, Email, AccountId
WHEN
LEAD
THEN
Id, Name, Company, AnnualRevenue
END,
WhatId, Subject, ActivityDate, Status, Priority,
Description,
TYPEOF What
WHEN

ls
Account
THEN
Id, Name, Rating

oo
WHEN
Opportunity
THEN
Id, Name, StageName, CloseDate
ch
END
FROM
Task
WHERE
rS
Who.Type IN ('Contact','Lead')
OR What.Type IN ('Account', 'Opportunity' )
];
for(Task t: taskList){
he

if(t.What instanceof Account){


Account acc = t.What;
}
if(t.Who instanceof Contact){
nt

Contact con = t.Who;


System.debug(con);
}
if(t.Who instanceof Lead){
Pa

Lead l = t.Who;
System.debug(l);
}
}
}

public static void aggregate(){


List<AggregateResult> agResult = [SELECT count(Id), AccountId
FROM Contact Group BY AccountId];
}

public static void query(List<Id> accountIdsSet){ // collections for


the filters
List<Account> accountList = [SELECT Id, Name FROM Account WHERE
Id =: accountIdsSet ];
for(Account acc: accountList){
System.debug(acc.Name);
// business
}
}

public static void query(String industry){


List<Account> accountList = [SELECT Id, Name FROM Account WHERE

ls
Industry =: industry ];
// >=:
// <=:

oo
// >:
// <:
System.debug(accountList);
List<Opportunity> opptyList = [SELECT Id, Name, Amount,
ch
CloseDate FROM Opportunity WHERE Name = 'ABVG' LIMIT 1];
System.debug(opptyList); // blank list
if(!opptyList.isEmpty()){
// do logic here
rS
}
List<Case> caseList = new List<Case>([SELECT Id, CaseNumber FROM
Case]);
}
he

}
nt
Pa
DAY21 - DML Operations in Salesforce

Recording Link
https://youtu.be/fDCFwQo49r8

Data Manipulation Language (DML)

Data Manipulation Language (DML) in Salesforce is used to manage data within the
Salesforce platform. DML operations allow you to perform various tasks such as
inserting, updating, deleting, and undeleting records in the Salesforce database.
These operations can be executed through Apex, Salesforce's proprietary

ls
programming language, or through standard Salesforce interfaces.

The DML operations are also known as CRUD Operations

oo
● Insert
● Update
● Delete


ch
Undelete
Upsert
● Merge
rS

Insert
he

public class DMLDemo {

public static void insertDemo(){


Account acc = new Account();
nt

acc.Name = 'PantherSchools_1.com';
acc.Rating = 'Warm';
acc.Phone = '9807654321';
acc.Active__c = 'Yes';
Pa

acc.AccountNumber = '9807654321';

Account acc1 = new Account();


acc1.Name = 'PantherSchools_2.com';
acc1.Rating = 'Warm';
acc1.Phone = '9807654321';
acc1.Active__c = 'Yes';
acc1.AccountNumber = '9807654321';

List<Account> accountList = new List<Account>();


accountList.add(acc);
accountList.add(acc1);
insert accountList; // Multiple Record
//insert new List<Account>{acc, acc1};
// 150 DML in a single transaction
}
public static void insert1(List<String> accountNames){
List<Account> accountList = new List<Account>();
Set<Account> accountSet = new Set<Account>();

for(Integer i=0; i< 160; i++){


Account acc = new Account(
Name = 'Salesforce.com'+i,
RATING = 'Hot'

ls
);
// Add the instance into the List/Set

oo
//insert acc; // Not Allowed/Recommended
accountSet.add(acc);
}
List<Account> accList = new List<Account>();
ch
accList.addAll(accountSet);
insert accList;

for(String accName: accountNames){


rS
Account acc = new Account();
acc.Name = accName;
acc.Rating = 'Warm';
acc.Phone = '9807654321';
acc.Active__c = 'Yes';
he

acc.AccountNumber = '9807654321';
//insert acc;
accountList.add(acc);
}
nt

//insert(accountList);
}
Pa

Update

public class DMLDemo {

public static void update1(){


Account acc = new Account(
AnnualRevenue = 234534,
Id = '001dL0000087eQ7QAI'
);
update acc;
}

Delete/Undelete

public class DMLDemo {

ls
public static void delete1(){
List<Account> accountList = [SELECT Id, Name FROM Account WHERE

oo
CREATEDDATE= today];
delete accountList;
List<Account> deletedRecords = [SELECT Id, Name
FROM Account WHERE CreatedDate = today ALL
ROWS];

}
ch
undelete deletedRecords;
rS
}

Conclusion
he

DML operations are a fundamental part of working with Salesforce data in Apex. By
understanding and using these operations effectively, developers can manipulate
Salesforce records to meet various business requirements. Always ensure to
nt

handle errors gracefully to maintain data integrity and provide a smooth user
experience.
Pa

Code used in Live Class

public class DMLDemo {

public static void insertDemo(){


Account acc = new Account();
acc.Name = 'PantherSchools_1.com';
acc.Rating = 'Warm';
acc.Phone = '9807654321';
acc.Active__c = 'Yes';
acc.AccountNumber = '9807654321';
Account acc1 = new Account();
acc1.Name = 'PantherSchools_2.com';
acc1.Rating = 'Warm';
acc1.Phone = '9807654321';
acc1.Active__c = 'Yes';
acc1.AccountNumber = '9807654321';

List<Account> accountList = new List<Account>();


accountList.add(acc);
accountList.add(acc1);
insert accountList; // Multiple Record
//insert new List<Account>{acc, acc1};

ls
// 150 DML in a single transaction
}

oo
public static void insert1(List<String> accountNames){
List<Account> accountList = new List<Account>();
Set<Account> accountSet = new Set<Account>();

ch
for(Integer i=0; i< 160; i++){
Account acc = new Account(
Name = 'Salesforce.com'+i,
RATING = 'Hot'
rS
);
// Add the instance into the List/Set
//insert acc; // Not Allowed/Recommended
accountSet.add(acc);
}
he

List<Account> accList = new List<Account>();


accList.addAll(accountSet);
insert accList;
nt

for(String accName: accountNames){


Account acc = new Account();
acc.Name = accName;
Pa

acc.Rating = 'Warm';
acc.Phone = '9807654321';
acc.Active__c = 'Yes';
acc.AccountNumber = '9807654321';
//insert acc;
accountList.add(acc);
}
//insert(accountList);
}

public static void update1(){


Account acc = new Account(
AnnualRevenue = 234534,
Id = '001dL0000087eQ7QAI'
);
update acc;
}

public static void delete1(){


List<Account> accountList = [SELECT Id, Name FROM Account WHERE
CREATEDDATE= today];
delete accountList;
List<Account> deletedRecords = [SELECT Id, Name
FROM Account WHERE CreatedDate = today ALL

ls
ROWS];
undelete deletedRecords;

oo
}

ch
Assignment

Assignment 1: Theoretical Understanding


rS

Question 1: Explain the lifecycle of a record in the Salesforce Recycle Bin.


Include details about the retention period.

Question 2: Describe the process and conditions under which a record is


he

permanently deleted from the Recycle Bin.

Question 3: Discuss the implications of the Recycle Bin's storage limit. How
nt

does it affect the deletion and recovery of records?

Assignment 2: Practical Exercises


Pa

Exercise 1: Deleting and Restoring Records

1. Create a new custom object named Project with fields Project Name
and Status.
2. Add several records to the Project object.
3. Delete one of the Project records.
4. Navigate to the Recycle Bin and restore the deleted Project record.
5. Verify that the restored record retains its original values.
Exercise 2: Manually Emptying the Recycle Bin

1. Delete multiple records from the Project object created in Exercise 1.


2. Go to the Recycle Bin and permanently delete all the records.
3. Confirm that the records cannot be recovered after being
permanently deleted.

Exercise 3: Handling Recycle Bin Limits

1. Determine the current storage limit of the Recycle Bin for your

ls
Salesforce organisation.
2. Create and delete enough records to approach the Recycle Bin's
storage limit.

oo
3. Observe and document the behaviour of the Recycle Bin when the
storage limit is reached. Note how Salesforce handles the oldest
records.
ch
Exercise 4: Using Apex to Interact with the Recycle Bin

● Write an Apex class that queries all deleted Project records.


rS
● Write an Apex method to restore a deleted Project record.

DAY22 - Inheritance in Salesforce


he

Recording Link
https://youtu.be/lFi2FnRzqWI
nt

Inheritance is a fundamental concept in object-oriented programming (OOP) that


allows one class (the child class) to inherit the properties and methods of another
Pa

class (the parent class). In Salesforce, Apex provides robust support for
inheritance, enabling developers to create hierarchical class structures that
promote code reuse, modularity, and maintainability.

Key Concepts of Inheritance in Salesforce Apex

1. Parent Class (Super Class)


● The class whose properties and methods are inherited by
another class.
● Declared normally as any other class in Apex.
2. Child Class (Sub Class)
● The class that inherits properties and methods from another
class.
● Declared using the extends keyword to denote inheritance.
3. Access Modifiers
● Access modifiers (public, private, protected) control the
visibility of properties and methods in the inheritance hierarchy.
● public: Accessible from any context.
● private: Accessible only within the same class.
● protected: Accessible within the same class and by classes that
inherit from it.
4. Constructors in Inheritance

ls
● Child classes do not inherit constructors from the parent class.
● If a child class constructor needs to call a parent class

oo
constructor, the super keyword is used.
5. Method Overriding
● A child class can provide a specific implementation of a
method that is already defined in its parent class.
ch
● The override keyword is used to define the overriding method.
rS
Extending a Class

You can extend a class to provide more specialised behaviour. Using virtual or
he

abstract keyword while declaring the Apex Class

public virtual class Marker {


nt

protected String markerName;


Pa

public Marker(String name){


this.markerName = name;
}

public virtual void write() {


System.debug('Writing some text.');
}

public virtual Double discount() {


return .05;
}

A class extends another class using the extends keyword in the class definition. A
class can only extend one other class, but it can implement more than one
interface.

ls
// Extension for the Marker class
public class YellowMarker extends Marker {

oo
public YellowMarker(String name){
super(name);
} ch
public override void write() {
System.debug('Writing some text using the yellow marker. '+markerName);
}
rS

}
he

Key Concepts
nt

● Static keyword
○ A static variable or method is a class-level construct that is initialised
only once and is associated with the outer class, along with its
Pa

initialization code. Such variables and methods can be accessed


directly using the class name, without the need to create an instance
of the class.
● Keywords
○ Final
■ This keyword is used to define constants and methods that
can’t be overridden.
○ super
■ This keyword invokes a constructor on a superclass. Refers the
Variables and Method of a superclass
○ this
■ This keyword represents the current instance of a class.
○ return
■ This keyword returns a value from a method.
○ null
■ This keyword defines a null constant that can be assigned to
any variable.

ls
Code used in the Apex Class

oo
global virtual class Marker {

protected String markerName;


ch
protected String color;

public Marker(String markerName, String color){


this.markerName = markerName;
rS
this.color = color;
}

public void write(){


he

System.debug('Marker is writing!');
}
global virtual void write_new(){
System.debug('Marker is writing!');
nt

}
global void sayHello(){
}
}
Pa

global class RedMarker extends Marker {

public RedMarker(String markerName, String color, String year,


String brandName){
super(markerName, color); // parent class constructor
}

public void printInfo(){


System.debug(super.markerName);
System.debug(super.color);
super.write();

super.write_new();
write_new();
this.write_new();

/* --- Will not work ----


* public void write(){

ls
}*/

public override void write_new(){

oo
System.debug('Red Marker is writing');
}

public void write_new(String color){

}
}
ch
System.debug('Red Marker is writing');
rS

Assignment
he

Assignment 1: Theoretical Understanding

Question 1: Explain the concept of inheritance in object-oriented


programming. How is it implemented in Salesforce Apex?
nt

Question 2: Describe the difference between a parent class and a child


class. Provide examples of how each is defined in Apex.
Pa

Question 3: What are the benefits of using inheritance in Salesforce


development? Discuss with examples.

Question 4: How do access modifiers (public, private, protected) impact


inheritance in Apex? Provide examples to illustrate your answer.
Assignment 2: Practical Exercises

Exercise 1: Basic Inheritance

1. Objective: Create a basic class hierarchy to understand the


fundamental concepts of inheritance.
2. Tasks:

ls
● Create a parent class named Vehicle with properties like make,
model, and year.
● Add a method displayInfo in the Vehicle class to display the

oo
vehicle details.
● Create a child class named Car that extends the Vehicle class
and adds an additional property numberOfDoors.
ch
● Override the displayInfo method in the Car class to include the
number of doors in the output.
● Write an Apex class to create instances of Car and call the
displayInfo method.
rS

Exercise 2: Method Overriding and Polymorphism


he

1. Objective: Understand method overriding and polymorphism by


creating a class hierarchy with multiple subclasses.
2. Tasks:
nt

● Create a parent class named Animal with a method makeSound.


● Create two child classes, Dog and Cat, that extend the Animal
class and override the makeSound method.
Pa

● Write an Apex class to demonstrate polymorphism by creating


a list of Animal objects that contains instances of Dog and Cat.
● Iterate over the list and call the makeSound method to show
how polymorphism works.
DAY23 - Abstract Classes in Salesforce

Recording Link
https://youtu.be/eQeq2_Z-zAA

Virtual Classes Points

● To create a virtual class, we need to use a virtual keyword


● You can extend a class to provide more sophisticated behaviour.
● Class that extends another class inherits all the methods and

ls
properties of the extended class.
● Methods declared as virtual can be overridden. In other words,

oo
overriding a virtual method allows you to provide a different
implementation for an existing method.
ch
● A class can only extend one other class, but it can implement more
than one interface.
● Virtual class can be initialise directly
rS
● You can use virtual or regular methods.
● Virtual class is some kind of “full” class. All methods are
implemented but can be overridden by the child class.
he

Abstract Class in Salesforce

In Salesforce Apex, an abstract class serves as a blueprint for other classes. It


cannot be instantiated on its own
nt

// Abstract class
public abstract class Shape {
Pa

Public String MY_VAR = ‘I can be defined’;


// Abstract method to calculate the area
public abstract Decimal calculateArea();

// Concrete method to display the area


public void displayArea() {
System.debug('The area is: ' + calculateArea());
}
}
// Subclass of Shape
public class Circle extends Shape {
private Decimal radius;

// Constructor
public Circle(Decimal radius) {
this.radius = radius;
}

// Implement the abstract method


public override Decimal calculateArea() {

ls
return Math.PI * radius * radius;
}
}

oo
// Subclass of Shape
public class Rectangle extends Shape {
ch
private Decimal length;
private Decimal width;

// Constructor
rS
public Rectangle(Decimal length, Decimal width) {
this.length = length;
this.width = width;
}
he

// Implement the abstract method


public override Decimal calculateArea() {
return length * width;
nt

}
}
Pa

// Usage example
public class AbstractClassExample {
public static void main() {
Shape circle = new Circle(5);
circle.displayArea(); // Output: The area is: 78.53981633974483

Shape rectangle = new Rectangle(4, 6);


rectangle.displayArea(); // Output: The area is: 24
}
}
Abstract Class Points

● To create an abstract class, we need to use an abstract keyword


● Allow extending the child's class.
● Abstract class can contain methods signed as abstract, to clarify, it
is a method that has only a signature (body is not defined).
● Child class must implement all methods declared as abstract!
● Abstract class can also include other methods, which have logic. To
allow child class access those methods use protected or public

ls
keywords.
● Cannot be initialise directly using new keyword

oo
● Abstract class can contain both virtual and abstract methods.
● Abstract class is some kind of “partial” class. Therefore, some
methods are implemented, and some need to be implemented by
ch
the child class.
● virtual methods can be overridden, but this is not mandatory.
rS
Assignment 1: Advanced Inheritance

Exercise 1: Abstract Class and Method Implementation


he

1. Objective: Learn how to use abstract classes and methods to define


a common interface for subclasses.
2. Tasks:
● Create an abstract class named Shape with an abstract method
nt

calculateArea.
● Create two subclasses, Circle and Rectangle, that extend the
Shape class and provide implementations for the calculateArea
Pa

method.
● Write an Apex class to create instances of Circle and Rectangle,
call the calculateArea method, and display the results.
DAY24 - What is Polymorphism

Recording Link
https://youtu.be/0Gv830CFmrA

What it is

A person at the same time can have different characteristics. Like a man, at the
same time is a father, a husband, and a team member. So the same person
possesses different behaviour in different situations. This is called polymorphism.

ls
oo
What is Method Overloading in Salesforce

If a class has multiple methods having the same name but different parameters, it
is known as Method Overloading.
ch
● Method Overloading
○ Does not Requires a Parent-Child Relationship
rS
○ method name should always be the same
○ method return type could be different
○ method parameter must be different or method parameter data
he

types must be different


○ All the methods should be within the same class
● Method Overriding
nt

○ Requires a Parent-Child Relationship


○ return type should be the same as the parent class method
○ method variables/parameters should be the same as the parent class
Pa

method
○ method name should always be the same
○ Uses override keyword in child class
○ in parent class, the method must be virtual

There are two ways to overload the method in Salesforce

● The number of arguments is different in the method


● Changing the data type of the arguments
public class Arithmetic {

public void sum(Integer a, Integer b){

public void sum(Integer a, Integer b, Integer c){

ls
public void sum(Integer a, Integer b, Integer c, Integer d){

oo
}

ch
public void sum(Integer a, Integer b, Integer c, Integer d, Integer e){

}
rS
}

Code used in the Class


he

public class DML {

public static void create(SObject record){


insert record;
nt

}
public static void create(List<sObject> records){
insert records;
Pa

}
}

Account acc = new Account();


acc.Name = 'PantherSchools.com';
acc.Rating = 'Hot';
acc.Industry = 'Education';

Account acc1 = new Account();


acc1.Name = 'academy.PantherSchools.com';
acc1.Rating = 'Hot';
acc1.Industry = 'Education';

List<Account> accList = new List<Account>();


accList.add(acc);
accList.add(acc1);
DML.create(accList);
System.debug(acc.Id);

Assignment

ls
Create a virtual Class named Vehicle and create the below methods

oo
1. start
2. stop
3. changeGear
ch
Create another class Mercedes that extends the Vehicle class and overrides the
above methods within the Mercedes class.
rS

Polymorphism in a Real-World Scenario

Objective: Apply polymorphism to a real-world scenario involving a shopping cart


he

system.

Tasks:
nt

1. Create an Abstract Class:


○ Define an abstract class Product with properties like name and
Pa

price and an abstract method calculateDiscount.


2. Create Subclasses:
○ Define two subclasses Electronics and Clothing that extend the
Product class.
○ Implement the calculateDiscount method in both subclasses to
provide specific discount logic.
3. Demonstrate Polymorphism:
○ Write an Apex class that creates instances of Electronics and
Clothing, stores them in a list of type Product, and calls the
calculateDiscount method on each instance.
4. Analyze Output:
○ Observe how the specific implementations of calculateDiscount
are called, demonstrating polymorphism.

Resources

ls
● https://www.jamessimone.net/blog/joys-of-apex/abstract-classes-and-interf
aces/

oo
● https://www.salesforcetutorial.com/static-final-this-super-keywords-in-apex
/
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/


ch
apex_classes_static.htm
https://www.javatpoint.com/method-overloading-in-java
● https://www.educative.io/answers/overloading-vs-overriding
rS
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_extending.htm
● https://atrium.ai/resources/what-is-an-interface-in-apex-salesforce-and-ho
w-to-use-it/
he

● https://beyondthecloud.dev/blog/abstract-virtual-interface-in-apex
● https://salesforce.stackexchange.com/questions/209510/whats-the-differenc
e-between-abstract-class-and-virtual-class
nt
Pa
Day 25 - Interface in Salesforce

Recording Link
https://youtu.be/bggLKC7xYHE

Interface
In programming, an interface can be thought of as a class that lacks the
implementation of its methods.

ls
● Custom Interface
● Standard interfaces (Database.Batchable, Comparable, System.Schedulable
etc)

oo
What is interface

ch
By providing a clear separation between a method declaration and implementation,
interfaces enable the addition of an abstraction layer to code. As a result, it
becomes possible to have different implementations of a method based on
rS
specific conditions.

● Within a class that is the interface, we can only define methods.


● The interface cannot provide the implementations for its method.
he

● The visibility of the methods will be controlled by the class that is


implementing the interface.
● In the interface, we cannot define any properties or variables except the
nt

methods.
● Multiple interfaces can be implemented by a Single class.
● A single interface can be implemented by Multiple Classes.
Pa

● An Interface can extend another interface.


● An interface can not be instantiated means object can not be created

Structure of Interface

// Interface Declaration

<Access Modifier> interface <Class Name>


public interface Vehicle{
public String methodName();
}

// Interface Declaration

<Access Modifier> interface <Class Name>


public interface Bank {
public String methodName();

ls
}

oo
Example

ch
As an example, we can take from the Banks where every bank has its own interest
for a different type of loans. Here is how the Apex Class will look like that is
implementing the interface
rS

Hands-On -

● Implement discounts using the IPurchaseOrder Interface.


he

● Implement Payment processing with IPaymentProcessor.

Interface
nt

public interface IBank{


public Decimal homeLoanInterest();
Pa

public Decimal carLoanInterest();


public Decimal personalLoanInterest();
public Decimal educationLoanInterest();
}

The class that implements the interface. To implement the interface, we need to
use implements keywords
public class SBIBank implements IBank{
public Decimal homeLoanInterest(){

}
public Decimal carLoanInterest(){

}
public Decimal personalLoanInterest(){

ls
}
public Decimal educationLoanInterest(){

oo
}
}
ch
Interface Key Points
rS

● To create an interface we need to use the interface keyword.


● Interface can provide a layer of abstraction to your code.
● Interface is an apex class that can contain only method signature, as
he

a result, the body of each method must be empty.


● An apex class that is using the interface must implement all
methods listed in the interface.
nt

● Interface separates the specific method declaration from its


implementation. Therefore you can have different implementations
Pa

for the same method. The concept behind Interface is that you can
change the implementation without changing your whole code.
Consequently, the method signature (return type, parameters) is
always the same.
● Interfaces can be treated as a new data type. In Apex we have a few
predefined interfaces like String, Integer, Double, etc. In other words
implementation of those methods can be changed (by Salesforce)
without changes in our code – that’s the power of interface! e.g.:
String.isBlank(‘Test’); We know method signature (public static
Boolean isBlank(String inputString)), but we don’t know
implementation layer.

Difference between Interfaces, Abstract & Virtual Keywords

Feature Interface Abstract Virtual

Keyword interface abstract virtual

e.g public e.g public abstract e.g public virtual

ls
interface class MyAbstractClass class MyVirtualClass
MyInterface {} {} {}

oo
How to By implements By extends By extends
use it? ch e.g public class
MyChildClass
e.g public class
MyChildClass extends
e.g public class
MyChildClass extends
implements MyAbstractClass {} MyVirtualClass {}
rS

MyInterface {}

Type of Only method Can contain abstract, Can contains only


he

Methods signature virtual and implements virual and


own methods. implements onw
e.g String
methods
nt

getFullName(Stri e.g abstract String


ng firstName, getFullName(String e.g public virtual
String lastName); firstName, String String
Pa

lastName); getFullName(String
firstName, String
public virtual String
lastName);
getFullName(String
firstName, String
lastName);
Feature Interface Abstract Virtual

Impleme ✅ All interface's ✅ Only methods ❌ Cannot force child


nt methods needs signed as abstract class to implements
Parent to be parent methods
Mehtho implemented
ds

❌ Interface ✅ Only methods ✅ Only methods

ls
Override
Parent doesn't contain signed as virtual signed as virtual

oo
Methods logic to override

Has
ch ❌ Interface ✅ Methods signed as ✅ Methods signed as
basic contains only virual and inner virual and inner
logic? method signature methods can contain methods can contain
basic logic basic logic
rS

Can it ❌ new ❌ new ✅ new


be MyInterface(); MyAbstractClass(); MyVirtualClass();
he

initialize
d
directly
nt

?
Pa
Feature Interface Abstract Virtual

Pros ✅ New layer of ✅ Abstract class can ✅ virtual class can


abstraction. store common logic be treated as a base

✅ Interface can (avoid redundancy) class with base logic,

be treated as a ✅ Child class can be which should be


accessed by all child
new data type. forced to implement
classes.

✅ Logic behind some (signed as


✅ Virtual class can

ls
abstract) methods.
the interface can
✅ Child class can be initialized direct

oo
be changed
without changes override some (signed ✅ Child class can
in related as virtual) methods. override some (signed
ch
classes.
✅ Basic logic can be as virtual methods)

✅ We program provided by abstract ✅ Easy to update


rS
to interfaces, not class inner methods. common code for all
implementations. (private, protected, child method. Just
Code doesn't public) update code in a
have reference to
✅ Easy to update virtual class.
he

specific classes.
common code for all Basic logic can be
✅ Child class is child methods. Just provided by abstract
nt

forced to update code in class inner methods.


implement the abstract class. (private, protected,
interface's public)
Pa

method
(Guarantee
specific logic).

✅ Child class
can implement
many interfaces.
Feature Interface Abstract Virtual

Cons ❌ Hard to add or ❌ Cannot be ❌ Cannot force child


change the initialized directly. class to implement
interface's
methods,
❌ Apex class needs to parent method
(abstract cannot be
implement all abstract
because all used)
methods even if it's
classes that
not relevant. ❌ Child class can be

ls
implement the
interface need to ❌ Child class can be only extended by one
parent class.

oo
be changed as only extended by one
well. parent class.

ch❌ Apex class


needs to
implement all
interface
rS

methods even if
it's not relevant.
he

Goal To create a new To provide a kind of To provide a kind of


layer of "partial" class with "full" class with base
abstraction common logic logic
nt
Pa

More details, examples of usage, and use cases can find


https://beyondthecloud.dev/blog/abstract-virtual-interface-in-apex
https://www.jamessimone.net/blog/joys-of-apex/abstract-classes-and-interfaces/
Code Used in the Class

public interface IProduct {


void calculateDiscount(Decimal price);
}

public interface ILogger {


void logError(String message);
void logInfo(String message);
Object log(List<sObject> records);

ls
}

oo
public interface IDocument extends ILogger {
void open(String fileName);
void read(String fileName);
void close(String fileName);
}
ch
public class Electronics implements IProduct, ILogger {
rS

public static void calculateDiscount(Decimal price){


System.debug('Discount is '+ (price * 0.1) );
}
he

public void calculateDiscount(Decimal price, Decimal percentage){


System.debug('Discount is '+ (price * percentage) );
}
nt

public void logError(String message){

}
public void logInfo(String message){
Pa

public Object log(List<sObject> records){


return null;
}
}

public class Clothings implements IProduct {

public static void calculateDiscount(Decimal price){


System.debug('Clothings Discount is '+ (price * 0.2) );
}
}

public class DML {

public interface IBulkCreate{

ls
public interface IAsyncCreate{

oo
public static void create(SObject record){
insert record;
} ch
public static void create(List<sObject> records){
insert records;
}
rS
}

Assignments
he

Assignment for Document Interface

1. Define an Interface:
nt

○ Create an interface Document with methods open, save, and close.


2. Implement Classes:
○ Create classes WordDocument and ExcelDocument that implement
Pa

the Document interface.


3. Write a Demonstration Class:
○ Write an Apex class that uses a list of Document interface references
to call methods on instances of WordDocument and ExcelDocument.

public interface Document {


void open();
void save();
void close();
}

Assignment for Notification Interface

Objective: Implement and demonstrate the use of the Notification interface with

ls
different notification types.

Tasks:

oo
1. Define the Interface:
○ Create an interface Notification with methods sendNotification
ch and scheduleNotification.
2. Implement Classes:
rS
○ Create classes EmailNotification and SMSNotification that
implement the Notification interface.
3. Write a Demonstration Class:
○ Write an Apex class that uses a list of Notification interface
he

references to call methods on instances of EmailNotification


and SMSNotification.
nt

public interface Notification {


void sendNotification(String message);
Pa

void scheduleNotification(String message, Datetime sendTime);


}

Assignment for Authentication Interface

Objective: Implement and demonstrate the use of the Authentication interface


with different authentication methods.

Tasks:

1. Define the Interface:


○ Create an interface Authentication with methods login and
logout.
2. Implement Classes:
○ Create classes LDAPAuthentication and OAuthAuthentication
that implement the Authentication interface.
3. Write a Demonstration Class:
○ Write an Apex class that uses a list of Authentication interface
references to call methods on instances of LDAPAuthentication and

ls
OAuthAuthentication.

public interface Authentication {

oo
Boolean login(String username, String password);
void logout();
}

Resources
ch
● https://www.jamessimone.net/blog/joys-of-apex/abstract-classes-and-interf
rS
aces/
● https://www.salesforcetutorial.com/static-final-this-super-keywords-in-apex
/
he

● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_static.htm
● https://www.javatpoint.com/method-overloading-in-java
nt

● https://www.educative.io/answers/overloading-vs-overriding
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_classes_extending.htm
Pa

● https://atrium.ai/resources/what-is-an-interface-in-apex-salesforce-and-ho
w-to-use-it/
● https://beyondthecloud.dev/blog/abstract-virtual-interface-in-apex
● https://salesforce.stackexchange.com/questions/209510/whats-the-differenc
e-between-abstract-class-and-virtual-class
DAY26 - Introduction to Apex Trigger

Recording Link
https://youtu.be/bSFavyBpGh0

Introduction to Apex Trigger


In Salesforce, ApexTrigger is a piece of code that is used to execute custom
business logic before or after certain events occur on records in a Salesforce
database.

ls
An ApexTrigger is associated with a specific object, such as an Account or Contact,
and is triggered by events like record creation, deletion, or updating. When the

oo
trigger is executed, it can perform a variety of actions, such as updating related
records, sending emails, or performing calculations.

ch
Syntax of Apex Trigger

trigger TriggerName on ObjectApiName (trigger events) {


rS

// Trigger code here


}
he

Where -
1. TriggerName - The Name of the ApexTrigger
2. ObjectApiName - The API Name of the Object
nt

3. TriggerEvents - The Trigger events could be any of the following(before, after)


- before insert
Pa

- after insert
- before update
- after update
- after undelete
- before delete
- after delete
trigger NewAccountTrigger on Account (before insert) {
// Loop through each new Account record being inserted
for (Account newAccount : Trigger.new) {
// Set a default value for the Account Rating field
newAccount.Rating = 'Hot';
}
}

Event Type of the Trigger

ls
There are two types of Apex Trigger Events in Salesforce

before

oo

● after

Trigger Events in Salesforce


ch
There are multiple apex trigger events for both before & after context

DML
rS

● insert
○ before
after
he


● update
○ before
○ after
nt

● delete
○ before
Pa

○ after
● undelete (insert =~ after insert )
○ after

- before insert
- after insert
- before update
- after update
- after undelete
- before delete
- after undelete

When to use before vs after trigger

ls
oo
ch
rS

Context Variables of Apex Triggers


he

Apex Triggers have access to several context variables that provide information
about the trigger event and the records involved. These context variables are
nt

automatically provided by Salesforce and can be used within the trigger code to
perform various operations.
Pa

These context variables provide a lot of information about the trigger event and
can be used to perform various operations, such as updating related records,
sending emails, or performing calculations.

Here are some of the most commonly used context variables in Apex Triggers:

● Trigger.new: A list of the new versions of the sObject records that have been
inserted or updated. This variable is only available in "before" triggers.
● Trigger.old: A list of the old versions of the sObject records before they were
updated. This variable is only available in "after" triggers.
● Trigger.newMap: A map of the new versions of the sObject records, keyed by
their record IDs.
○ Map<Id, sObject>
● Trigger.oldMap: A map of the old versions of the sObject records before they
were updated, keyed by their record IDs.
○ Map<Id, sObject>
● Trigger.isInsert: A Boolean value indicating whether the trigger was fired

ls
due to an insert event.
● Trigger.isUpdate: A Boolean value indicating whether the trigger was fired

oo
due to an update event.
● Trigger.isDelete: A Boolean value indicating whether the trigger was fired
due to a delete event.
● ch
Trigger.isUnDelete: A Boolean value indicating whether the trigger was fired
due to an undelete event.
● Trigger.isBefore: A Boolean value indicating whether the trigger was fired
rS
before the data was saved to the database.
● Trigger.isAfter: A Boolean value indicating whether the trigger was fired
after the data was saved to the database.
● Trigger.size: An integer value indicating the number of records involved in
he

the trigger event. Max - 200


● Trigger.operationType - The current operation of the Apex Trigger can be
identified by obtaining an enumeration of type System.TriggerOperation.
nt

Possible Values are - BEFORE_INSERT, BEFORE_UPDATE,


BEFORE_DELETE,AFTER_INSERT, AFTER_UPDATE, AFTER_DELETE, and
Pa

AFTER_UNDELETE.
● Trigger.isExecuting - To determine if the current context for the Apex code
is a trigger and not a Visualforce page, Web service, or execute anonymous
API call, check if the returned value is true.

trigger MyCaseTrigger on Case (before insert) {


System.debug(Trigger.New);
System.debug(Trigger.Old);
System.debug(Trigger.NewMap);
System.debug(Trigger.oldMap);
System.debug(Trigger.isBefore);
System.debug(Trigger.isAfter);
System.debug(Trigger.isDelete);
System.debug(Trigger.isUnDelete);
System.debug(Trigger.isUpdate);
System.debug(Trigger.isInsert);
System.debug(Trigger.isExecuting);

ls
System.debug(Trigger.operationType );
System.debug(Trigger.size);

oo
}

ch
trigger LeadTrigger on Lead (before insert) {
System.debug(Trigger.New); // YES
System.debug(Trigger.Old); // null
System.debug(Trigger.NewMap); // null
rS
System.debug(Trigger.oldMap); // null
System.debug(Trigger.isBefore); // True
System.debug(Trigger.isAfter); // False
System.debug(Trigger.isDelete); // False
System.debug(Trigger.isUnDelete); // False
he

System.debug(Trigger.isUpdate);// False
System.debug(Trigger.isInsert); // True
System.debug(Trigger.isExecuting); // True
System.debug(Trigger.operationType ); // BEFORE_INSERT
nt

System.debug(Trigger.size);
}
Pa

When a Context Variable is available in the Apex Trigger Events

Context Variable before after before after before after after


insert insert update update delete delete undeleted

Trigger.New ✅ ✅ ✅ ✅ ✅
Trigger.NewMap ✅ ✅ ✅
Trigger.old ✅ ✅ ✅ ✅
Trigger.oldMap ✅ ✅ ✅ ✅

Resources

● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_triggers.htm

ls
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers
● https://trailhead.salesforce.com/content/learn/projects/quick-start-apex-co

oo
ding-for-admins/create-a-trigger
● https://trailhead.salesforce.com/content/learn/modules/apex_testing/apex_t
esting_triggers
● ch
https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_t
riggers_bulk

Day 27 - Practical Implementation for the Apex Triggers


rS

Recording Link
https://youtu.be/jAwX0OZyzao
he

Apex Trigger Scenarios


https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
nt

0GFoFc/edit?usp=sharing

Mantra to solve any problem


Pa

● Read the problem, Again read the problem, and Again read the problem
○ Able to understand the problem?
■ YES
■ No
■ Read the Problem Statement again
■ Take help from your lead/friends/colleague
● Ask Question ( The person who has given the problem )
○ Remember - No questions is a silly or dumb question
● Again read the problem
○ Repeat until not able to understand
● Got a good understanding of the problem
○ Start Solving
■ Break the problem into multiple small problems ( Apple
Problem / Divide & Conquer / Divide, Solve & Merge )
■ Solve the individual problem
■ Merge all the problems into a single unit ( Class / Trigger
/ LWC )

ls
Questions to develop apex trigger

● Which object

oo
○ Account
● What Event
○ before insert


ch
○ before update
What functionality
○ You only need to update the Shipping Address information with Billing
rS
Address if the Shipping Address is having a blank value.

Apex Trigger Scenario #1


he

Develop an Apex Trigger so that every time when any account is inserted then set
the value of the Industry field to Education.
nt

1. Industry = Education
Pa

Questions -

● Which object
○ Account
● What Event
○ before
● Which DML
○ insert
● What functionality
○ The industry should be an Education
trigger AccountTrigger on Account (before insert) {
List<Account> newRecords = Trigger.New; // ~List<Account>
for(Account acc : newRecords){
acc.Industry = 'Education';
acc.Phone = '9878346445';
acc.Description = 'I am from apex trigger!';
}
//Account accRecord = newRecords.get(0);
//accRecord.Industry = 'Education';
}

ls
Apex Trigger Scenario #2

oo
Develop an Apex Trigger so that every time when any account is inserted then
check if the Account Industry is blank then set the value of description to
“Account Industry is blank, Please provide the value ASAP.”
ch
Questions -
rS
● Which object
○ Account
● What Event
○ before
he

● Which DML
○ insert
● What functionality
nt

○ If the Industry is blank


○ Set the value for description to “Account Industry is blank, Please
Pa

provide the value ASAP.”

trigger AccountTrigger on Account (before insert) {


List<Account> newRecords = Trigger.New; // ~List<Account>
for(Account acc : newRecords){
if(String.isBlank(acc.Industry)){
acc.Description = 'Account Industry is blank, please update
it ASAP!';
}
}
}
Apex Trigger #3

Develop an Apex Trigger so that every time when any account is created or
updated then Set the Value of the Billing Address is to Shipping Address.

Shipping Address Fields

● ShippingStreet
● ShippingCity
● ShippingState

ls
● ShippingPostalCode
● ShippingCountry

oo
Billing Address Fields

● BillingStreet


ch
BillingCity
BillingState
● BillingPostalCode
rS
● BillingCountry

Questions
he

● Which object
○ Account
● What Event
nt

○ before
● Which DML
○ insert
Pa

○ update
● What functionality
○ Set the Value of the Billing Address to Shipping Address.

trigger AccountTrigger on Account (before insert, before update) {

if(Trigger.isBefore && Trigger.isInsert){


List<Account> newRecords = Trigger.New; // ~List<Account>
for(Account acc : newRecords){
if(String.isBlank(acc.Industry)){ // #1
acc.Description = 'Account Industry is blank, please update it
ASAP!';//#2
}
/** Populate billing address into the shipping address */
acc.ShippingStreet = acc.BillingStreet;
acc.ShippingState = acc.BillingState;
acc.ShippingCountry = acc.BillingCountry;
acc.ShippingPostalCode = acc.BillingPostalCode;
acc.ShippingCity = acc.BillingCity;
}

ls
} else if(Trigger.isBefore && Trigger.isUpdate){
List<Account> newRecords = Trigger.New; // ~List<Account>

oo
for(Account acc : newRecords){
if(String.isBlank(acc.Industry)){ // #1
acc.Description = 'Account Industry is blank, please update it
ASAP!';//#2
ch
}
acc.ShippingStreet = acc.BillingStreet;
acc.ShippingState = acc.BillingState;
acc.ShippingCountry = acc.BillingCountry;
rS
acc.ShippingPostalCode = acc.BillingPostalCode;
acc.ShippingCity = acc.BillingCity;
}
}
he

/*if(Trigger.isBefore){
if(Trigger.isInsert){

} else if(Trigger.isUpdate){
nt

}
} else {
Pa

}*/

Assignment

Develop an Apex Trigger so that every time when any account is created or
updated then Set the Value of the Billing Address is to Shipping Address.
You only need to update the Shipping Address information with Billing Address if
the Shipping Address (Compound Field) is having a blank value.

Ask Questions if there are any

● Shall we need to check all the fields are blank and then only update - YES (
AND && )
● OR if any field is blank then update all the fields - YES ( OR || )
● OR if any field is blank then only update that field value
○ nested if else for individual fields

ls
Resources

oo
● https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/obj
ect_reference/compound_fields_address.htm
ch
Day 28 - Practical Implementation for the Apex Triggers
rS

Recording Link
https://youtu.be/osaH81Ozg1c
he

Apex Trigger Scenarios


https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing
nt

Apex Trigger Scenario


Pa

Develop an Apex Trigger so every time when a Contact record is created check if
the Contact has the related account populated, if the account is populated then
populate the Account Billing Address into the Contact Mailing Address
Solution #1

trigger ContactTrigger on Contact (before insert) {

Set<Id> accountIdsSet = new Set<Id>();


for(Contact con: Trigger.New){
if(con.AccountId != null){
accountIdsSet.add(con.AccountId );

ls
}
}
List<Account> accountList = [SELECT Id, Name, BillingStreet,

oo
BillingState, BillingCity,
BillingCountry, BillingPostalCode FROM
Account
WHERE Id IN: accountIdsSet
ch
// 10 Accounts
];

for(Contact con: Trigger.New){ // ABC


if(con.AccountId != null){
rS
for(Account acc: accountList){ // XYZ
if(con.AccountId == acc.Id){
con.MailingStreet = acc.BillingStreet;
con.MailingState = acc.BillingState;
he

}
}
}
}
nt

}
Pa

Solution #2

trigger ContactTrigger on Contact (before insert) {

Set<Id> accountIdsSet = new Set<Id>();


for(Contact con: Trigger.New){
if(con.AccountId != null){
accountIdsSet.add(con.AccountId );
}
}

Map<Id, Account> accountIdToAccountMap = new Map<Id,


Account>([SELECT Id, Name, BillingStreet, BillingState, BillingCity,
BillingCountry, BillingPostalCode FROM
Account
WHERE Id IN: accountIdsSet
]);
// Common field will always be the key of your map
// Information that you need will be the value of the map
// 10 Accounts
for(Contact con: Trigger.New){ // ABC
if(accountIdToAccountMap.containsKey(con.AccountId)){
Account acc = accountIdToAccountMap.get(con.AccountId);

ls
con.MailingStreet = acc.BillingStreet;
con.MailingState = acc.BillingState;
}

oo
}
}

ch
Assignment

Develop an Apex Trigger so every time when an Opportunity record is created check
rS
if the Opportunity has the related account populated, if the account is populated
then populate the Account Description into the Opportunity Description.
he

Day 29 - Practical Implementation for the Apex Triggers


nt

Recording Link
https://youtu.be/SNMwvu4_CSE
Pa

Apex Trigger Scenarios


https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing

Apex Trigger Scenario

When the Account is Created, create a Task Record under that Account and assign
the Task to the Account Owner. Use the below information

● Subject - Created from Apex Trigger


● Comments - Created from Apex Trigger
● Due Date - Today's Date + 7
● Status - Not Started
● Priority - High
● Related To (What) - Account Id
● Assigned To (OwnerId) - Account Owner Id

Question -

● Which object

ls
○ Account
● What Event

oo
○ after
● Which DML
○ insert

ch
What functionality ( Want to Achieve )
○ Task Record under that Account and assign the Task to the Account
Owner
rS

trigger AccountTrigger on Account (before insert, before update, after


insert, after update) {
he

if(Trigger.isBefore && Trigger.isInsert){


List<Account> newRecords = Trigger.New; // ~List<Account>
for(Account acc : newRecords){
if(String.isBlank(acc.Industry)){ // #1
nt

acc.Description = 'Account Industry is blank, please


update it ASAP!';//#2
}
Pa

/** Populate billing address into the shipping address */


acc.ShippingStreet = acc.BillingStreet;
acc.ShippingState = acc.BillingState;
acc.ShippingCountry = acc.BillingCountry;
acc.ShippingPostalCode = acc.BillingPostalCode;
acc.ShippingCity = acc.BillingCity;
}
} else if(Trigger.isBefore && Trigger.isUpdate){
List<Account> newRecords = Trigger.New; // ~List<Account>
for(Account acc : newRecords){
if(String.isBlank(acc.Industry)){ // #1
acc.Description = 'Account Industry is blank, please
update it ASAP!';//#2
}
acc.ShippingStreet = acc.BillingStreet;
acc.ShippingState = acc.BillingState;
acc.ShippingCountry = acc.BillingCountry;
acc.ShippingPostalCode = acc.BillingPostalCode;
acc.ShippingCity = acc.BillingCity;
}
} else if(Trigger.isAfter && Trigger.isInsert){
List<Task> taskList = new List<Task>();
List<Account> newRecords = [SELECT Id, Name, AccountNumber FROM

ls
Account WHERE ID IN: Trigger.New ];
for(Account acc: Trigger.New){

oo
/** Insert Task Record **/
Task taskRecord = new Task();
taskRecord.Subject = 'Created from Apex Trigger '+acc.Name;
taskRecord.Description = 'Created from Apex Trigger
ch
'+acc.Name;
taskRecord.ActivityDate = System.today().addDays(7);
taskRecord.Status = 'Not Started';
taskRecord.Priority = 'High';
rS
taskRecord.WhatId = acc.Id;
taskRecord.OwnerId = acc.OwnerId;
taskList.add(taskRecord);
//insert taskRecord;
he

// Date todaysDate = System.today();


// DateTime currentDateTime = System.now();
}
insert taskList;
nt

for(Account acc: newRecords){


acc.AccountNumber = '867346345';
acc.Rating = 'Warm';
Pa

}
update newRecords;
} else if(Trigger.isAfter && Trigger.isUpdate){
for(Account acc: Trigger.New){
//acc.AccountNumber = '35534565';
}
}

/*if(Trigger.isBefore){
if(Trigger.isInsert){

} else if(Trigger.isUpdate){
}
} else {

}*/

List<Account> accList = new List<Account>();


for(Integer i=0; i< 200; i++){
Account acc = new Account(

ls
Name = 'Amazon.com '+i
);
accList.add(acc);

oo
}

insert accList;
ch
Assignment
rS

● Develop an Apex Trigger that will update the Contact Phone Value to
Contact Fax every time a Contact is updated.
● Develop an Apex Trigger so that when an opportunity is created and the
he

account is blank, create a task under Opportunity. Hint: - AccountId == null


○ Subject - Opportunity is created without Account
○ Description - Opportunity is created without Account, Please assign
nt

the Correct account to Opportunity.


○ Due Date - Today's Date + 7
Pa

○ Status - Not Started


○ Priority - High
○ Related To (What) - Opportunity Id
○ Assigned To (OwnerId) - Opportunity Owner Id
● Create an Apex Trigger on Case Object and print all the Trigger Context
Variable

Resources
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_triggers.htm
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers
● https://trailhead.salesforce.com/content/learn/projects/quick-start-apex-co
ding-for-admins/create-a-trigger
● https://trailhead.salesforce.com/content/learn/modules/apex_testing/apex_t
esting_triggers
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_t
riggers_bulk

ls
DAY30 - Dispatcher Concept in Salesforce Apex Trigger

oo
Recording Link
https://youtu.be/2f0tHINaxBU

ch
Apex Trigger Scenarios
https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing
rS
Code used in Apex Class

trigger AccountTrigger on Account (before insert, before update, after


insert, after update, before delete, after delete, after undelete) {
he

AccountTriggerDisptacher.run(Trigger.OperationType);
nt

/* if(Trigger.isBefore && Trigger.isInsert){ // before insert

} else if(Trigger.isBefore && Trigger.isUpdate){ // before update


Pa

} else if(Trigger.isAfter && Trigger.isInsert){ // after insert

} else if(Trigger.isAfter && Trigger.isUpdate){

if(Trigger.isBefore){
if(Trigger.isInsert){

} else if(Trigger.isUpdate){

}
} else {

}*/

public class AccountTriggerDisptacher {

public static void run(System.TriggerOperation opType){


switch on opType{
when BEFORE_INSERT{

ls
// call the handler class method
AccountTriggerHandler.handleBeforeInsert(Trigger.New);
}

oo
when AFTER_INSERT{

}
ch when BEFORE_UPDATE{

}
AccountTriggerHandler.handleBeforeUpdate(Trigger.New);

when AFTER_UPDATE{
rS
}
when BEFORE_DELETE{

}
he

when AFTER_DELETE{
AccountTriggerHandler.handleAfterDelete((Map<Id,
Account>)Trigger.newMap );
}
nt

when AFTER_UNDELETE{

}
Pa

when ELSE{

}
}
}
}

public class AccountTriggerHandler {


public enum LOGGER {
DEBUG, WARN, INFO, SUCCESS
}
public static void handleBeforeInsert(List<Account> newRecords){ //
parameters
//List<Account> newRecords = Trigger.New; // ~List<Account>
for(Account acc : newRecords){
if(String.isBlank(acc.Industry)){ // #1
acc.Description = 'Account Industry is blank, please
update it ASAP!';//#2
}
/** Populate billing address into the shipping address */

ls
acc.ShippingStreet = acc.BillingStreet;
acc.ShippingState = acc.BillingState;
acc.ShippingCountry = acc.BillingCountry;

oo
acc.ShippingPostalCode = acc.BillingPostalCode;
acc.ShippingCity = acc.BillingCity;
}
System.debug(LOGGER.DEBUG); // DEBUG
} ch
public static void handleBeforeUpdate(List<Account> newRecords){
//List<Account> newRecords = Trigger.New; // ~List<Account>
rS
for(Account acc : newRecords){
if(String.isBlank(acc.Industry)){ // #1
acc.Description = 'Account Industry is blank, please
update it ASAP!';//#2
he

}
acc.ShippingStreet = acc.BillingStreet;
acc.ShippingState = acc.BillingState;
acc.ShippingCountry = acc.BillingCountry;
nt

acc.ShippingPostalCode = acc.BillingPostalCode;
acc.ShippingCity = acc.BillingCity;
}
}
Pa

public static void handleAfterInsert(List<Account> newRecordList){


List<Task> taskList = new List<Task>();
List<Account> newRecords = [SELECT Id, Name, AccountNumber FROM
Account WHERE ID IN: newRecordList ];
//List<Account> accList = trigger.new;
for(Account acc: newRecordList){

/** Insert Task Record **/


Task taskRecord = new Task();
taskRecord.Subject = 'Created from Apex Trigger '+acc.Name;
taskRecord.Description = 'Created from Apex Trigger
'+acc.Name;
taskRecord.ActivityDate = System.today().addDays(7);
taskRecord.Status = 'Not Started';
taskRecord.Priority = 'High';
taskRecord.WhatId = acc.Id;
taskRecord.OwnerId = acc.OwnerId;
taskList.add(taskRecord);
//insert taskRecord;
// Date todaysDate = System.today();
// DateTime currentDateTime = System.now();
}

ls
insert taskList;

for(Account acc: newRecords){

oo
acc.AccountNumber = '867346345';
acc.Rating = 'Warm';
}
update newRecords;
} ch
public static void handleAfterUpdate(Map<Id, Account> oldMap,
Map<Id, Account> newMap){
rS
/**
* new
* old
* newMap
he

* oldMap
*/
}
nt

public static void handleAfterDelete(Map<Id, Account>


accountIdToAccountMap){
//Map<Id, sObject> accountIdMap = Trigger.newMap;
}
Pa

public static void handleAfterUndelete(Map<Id, Account>


accountIdToAccountMap){
// trigger.new - List
// Trigger.newMap -> Set & List
// accountIdToAccountMap.value(); // List<Account>
// accountIdToAccountMap.keySet(); // Set<Id>
}
}
Assignment

● Convert the Apex Trigger that you created in the previous assignment to
Handler & Dispatcher Apex Classes
● Develop an Apex Trigger on the Contact record so that when any Contact is
created under any Account Record then, please
○ Populate the Contact Mailing Address with Account Shipping Address
○ Populate the Contact Other Address with the Account Billing Address
○ Make Sure you are following the Dispatcher & Handler Class concept.

ls
Apex Trigger Assignment

oo
● Create a Custom field on Opportunity “Discount” and the data type of this
field should be percent.
● Create a Custom field on Opportunity “Price After Discount” with Currency
ch
Data Type

Develop an Apex Trigger on Opportunity so that if the Discount & Amount field is
not blank then calculate the discount and store it in the Discount Price field.
rS

To Calculate the discount use the below formula and store it in a variable
Decimal discount = ( Discount * Amount ) / 100
he

To Calculate the Discounted Price(Price After Discount) use the below calculation
and store it in a variable
nt

Decimal priceAfterAmount = Amount - discount;

Resources
Pa

● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_triggers.htm
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers
● https://trailhead.salesforce.com/content/learn/projects/quick-start-apex-co
ding-for-admins/create-a-trigger
● https://trailhead.salesforce.com/content/learn/modules/apex_testing/apex_t
esting_triggers
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_t
riggers_bulk
Day 31 - Duplicate Contact Record using Apex Trigger Problem Part 1

Recording Link
https://youtu.be/V3znV6nR7R8

Apex Trigger Scenarios


https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing

The business got to know that there are multiple contacts with the same emails

ls
being created. Now, as a developer, you need to make sure that no new duplicates
are being created with the same email.

oo
[email protected]
[email protected] - duplicate contact
ch
Questions?

● Which Object?
rS
○ Contact
● Which Event?
○ Before Insert
he

○ Before Update
○ After Undelete
● The problem that we are solving?
nt

○ Prevent the duplicate records based on the contact email.

Code Used in Apex Class


Pa

public class ContactTriggerHelper {

public static void preventDuplicateContactBasedOnEmail(List<Contact>


newRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
// add the email to set
if(String.isBlank(con.Email) == false){
// null, ''
emailSet.add(con.Email);
} else {
con.Email.addError('Email can not be blank!');
}
}

ls
List<Contact> existingContactList = [SELECT Id, Name, Email FROM
Contact WHERE Email IN: emailSet];
for(Contact con : newRecords ){

oo
// #2 - Better Approach than #1 (Not Recommended)
for(Contact existingRecord: existingContactList){
if(con.Email == existingRecord.Email){ // duplicate
criteria
ch
'+con.Email);
con.addError('Duplicate contact found! '+con.Email);
//existingRecord.addError('Duplicate contact found!

con.Email.addError('Duplicate contact found!


rS
'+con.Email);
}
}
// #1 Approach (Not Recommended)
he

/*if(existingContactList.size()>0){ // [email protected]
con.addError('Duplicate contact found!');
con.Email.addError('Duplicate contact found!');
}*/
nt

}
/**
* [email protected] - Not a duplicate record
* [email protected] - is a duplicate record
Pa

*/

}
}

Assignment

Create an Apex Trigger to prevent the duplicate lead record based on the Lead
Email and Company. Please cover all the scenarios which can create the duplicate
records.
Day 32 - Duplicate Contact Record using Apex Trigger Problem Part 2

Recording Link
https://youtu.be/e9Uy2y3CaYA

Apex Trigger Scenarios


https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing

The business got to know that there are multiple contacts with the same emails

ls
being created. Now, as a developer, you need to make sure that no new duplicates
are being created with the same email.

oo
[email protected]
[email protected] - duplicate contact
ch
Questions?

● Which Object?
rS
○ Contact
● Which Event?
○ Before Insert
he

○ Before Update
○ After Undelete
● The problem that we are solving?
nt

○ Prevent the duplicate records based on the contact email.

Code used in the apex class


Pa

The below apex trigger helper class is having redundant code which has been
removed in the next version of the code. Updated code is given below.

public class ContactTriggerHelper {

public static void


preventDuplicateContactBasedOnEmailAfterUndelete(List<Contact>
newRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
if(String.isBlank(con.Email) == false){ //
[email protected]
emailSet.add(con.Email); //
[email protected]

ls
}
}
// 003dL000001iwXZQAY

oo
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record

}
ch emailToContactMap.put(con.Email, con);

System.debug('emailToContactMap \n '+emailToContactMap);
for(Contact con : newRecords ){
rS
// #3 - Recommended
if(emailToContactMap.containsKey(con.Email)){ //
[email protected]
con.addError('Duplicate contact found! '+con.Email);
he

con.Email.addError('Duplicate contact found!


'+con.Email);
} else {
emailToContactMap.put(con.Email, con);
nt

}
}
}
Pa

public static void


preventDuplicateContactBasedOnEmailBeforeUpdate(List<Contact>
newRecords, Map<Id, Contact> oldRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
// Check if the Email is Changed
Contact oldRecord = oldRecords.get(con.Id);
// add the email to set
// [email protected] --> Old Email
// [email protected] --> New/Updated Email
if(oldRecord.Email <> con.Email && String.isBlank(con.Email)
== false){ // [email protected]
emailSet.add(con.Email); //
[email protected]
} else {
con.Email.addError('Email can not be blank!');

ls
}
}
// 003dL000001iwXZQAY

oo
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record

}
ch emailToContactMap.put(con.Email, con);

System.debug('emailToContactMap \n '+emailToContactMap);
for(Contact con : newRecords ){
rS
// #3 - Recommended
if(emailToContactMap.containsKey(con.Email)){ //
[email protected]
con.addError('Duplicate contact found! '+con.Email);
he

con.Email.addError('Duplicate contact found!


'+con.Email);
} else {
emailToContactMap.put(con.Email, con);
nt

}
}
}
Pa

public static void preventDuplicateContactBasedOnEmail(List<Contact>


newRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
// add the email to set
if(String.isBlank(con.Email) == false){
// null, ''
emailSet.add(con.Email);
} else {
con.Email.addError('Email can not be blank!');
}
}

Map<String, Contact> emailToContactMap = new Map<String,


Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE

ls
Email IN: emailSet]){
emailToContactMap.put(con.Email, con);
}

oo
for(Contact con : newRecords ){
// #3 - Recommended
if(emailToContactMap.containsKey(con.Email)){ //
ch
[email protected]
con.addError('Duplicate contact found! '+con.Email);
//existingRecord.addError('Duplicate contact found!
'+con.Email);
rS
con.Email.addError('Duplicate contact found!
'+con.Email);
} else {
emailToContactMap.put(con.Email, con);
he

}
}
// #1 - [email protected]
// does email found - No
nt

// Add a new entry in the Map


// #2 - [email protected]
// does email found - Yes
Pa

}
}

ContactTrigger Code

trigger ContactTrigger on Contact (before insert, before update, after


undelete) {

/** US-8783 : Added by Amit Singh on 18-06-2024, to prevent


duplicate records based on Email */
if(trigger.isBefore && Trigger.isInsert){

ContactTriggerHelper.preventDuplicateContactBasedOnEmail(Trigger.New);
} else if(Trigger.isBefore && Trigger.isUpdate){

ContactTriggerHelper.preventDuplicateContactBasedOnEmailBeforeUpdate(Tri
gger.New, (Map<Id, Contact>)Trigger.oldMap );
} else if(Trigger.isAfter && Trigger.isUpdate){

ContactTriggerHelper.preventDuplicateContactBasedOnEmailBeforeUpdate(Tri
gger.New, (Map<Id, Contact>)Trigger.oldMap );

ls
} else if(Trigger.isAfter && Trigger.isUndelete){

ContactTriggerHelper.preventDuplicateContactBasedOnEmailAfterUndelete(Tr

oo
igger.New);
}
}

ch
Optimised Apex Code
rS
public class ContactTriggerHelper {

public static void


preventDuplicateContactBasedOnEmailAfterUndelete(List<Contact>
he

newRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
nt

* 1. Get the Existing Records of Contact Object based on Email


* 2. Check if the record found
* 3. Add Error
*/
Pa

Set<String> emailSet = new Set<String>();


for(Contact con : newRecords){
if(String.isBlank(con.Email) == false){ //
[email protected]
emailSet.add(con.Email); //
[email protected]
}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}

public static void


preventDuplicateContactBasedOnEmailBeforeUpdate(List<Contact>
newRecords, Map<Id, Contact> oldRecords){
// Check the duplicate records based on Email

ls
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email

oo
* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
ch
for(Contact con : newRecords){
// Check if the Email is Changed
Contact oldRecord = oldRecords.get(con.Id);
// add the email to set
rS
// [email protected] --> Old Email
// [email protected] --> New/Updated Email
if(oldRecord.Email <> con.Email && String.isBlank(con.Email)
== false){ // [email protected]
he

emailSet.add(con.Email); //
[email protected]
} else {
con.Email.addError('Email can not be blank!');
nt

}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
Pa

Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
// Email+Company
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}

public static void preventDuplicateContactBasedOnEmail(List<Contact>


newRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
// add the email to set

ls
if(String.isBlank(con.Email) == false){
// null, ''
emailSet.add(con.Email);

oo
} else {
con.Email.addError('Email can not be blank!');
}
}
ch
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
rS
Email IN: emailSet]){
emailToContactMap.put(con.Email, con);
}
he

checkDuplicate(newRecords, emailToContactMap);
// #1 - [email protected]
// does email found - No
// Add a new entry in the Map
nt

// #2 - [email protected]
// does email found - Yes
}
Pa

public static void checkDuplicate(List<Contact> newRecords,


Map<String, Contact> emailToContactMap){
for(Contact con : newRecords ){
// #3 - Recommended
// String key = Email+Company
if(emailToContactMap.containsKey(con.Email)){ //
[email protected]
con.addError('Duplicate contact found! '+con.Email);
con.Email.addError('Duplicate contact found!
'+con.Email);
} else {
emailToContactMap.put(con.Email, con);
}
}
}
}

Anonymous Apex Code

List<Contact> conList = new List<Contact>();


for(Integer i=0; i< 7; i++){

ls
Contact con = new Contact(
FirstName = 'Test '+i,
LastName = 'Test '+i,

oo
Email = '[email protected]'
);
conList.add(con);
}
ch
insert conList;
rS
Assignment

Create an Apex Trigger to prevent the duplicate lead record based on the Lead
Email and Company. Please cover all the scenarios which can create the duplicate
he

records.

Apex Trigger Scenario


nt

Prerequisite
Pa

● Create a Custom Object and Name it “Location”. Relate this location object
with Account using Lookup relationship.
● Create a field on Account “Number of Locations” of type Number

Develop a solution that will create the location records when the Account is
created and the “Number of Locations” field has some value in it. The number of
locations related to the account should be the same as the value in the “Number
of Locations” field.
For Example - if the value of the “Number of Locations” field is 4 then there
should be 4 location records created under that account.

Apex Trigger Scenario

Develop an Apex Trigger to prevent the duplicate Contact record using Name &
Email. So if there are duplicate records with the same Name & Email new Record
should not get created.

ls
Apex Trigger Scenario

oo
Develop an Apex Trigger to prevent Duplicate Leads if there is already an existing
Lead record with the same Email & Company.

Resources
ch
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
rS
apex_triggers.htm
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers
● https://trailhead.salesforce.com/content/learn/projects/quick-start-apex-co
he

ding-for-admins/create-a-trigger
● https://trailhead.salesforce.com/content/learn/modules/apex_testing/apex_t
esting_triggers
nt

● https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_t
riggers_bulk
Pa

Day 33 - Rollup Summary Apex Trigger in Salesforce

Recording Link
https://youtu.be/LVHLvxBkL8w

Apex Trigger Scenarios


https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing
Prerequisite

Create a Custom Field on the Account Object “Number of Contacts” with a


Number Data Type that does not have any decimal point in it.

Requirement (Roll-up Summary Trigger)

Develop a Solution that will count the related contact related to the Account and
store the information in the “Number of Contacts” field.

Note:- The contact can be created/deleted/undeleted and updated from the

ls
Account Record. So please keep that in mind

oo
Questions to be Asked?

Question?


ch
Which Object
○ Contact
rS
● Events
○ after insert, after delete, after undelete, after update
● Functionality
○ Develop a Solution that will count the related contact related to the
he

Account and store the information in the “Number of Contacts” field.

Apex Class
nt

public class ContactTriggerHelper {


Pa

public static void


preventDuplicateContactBasedOnEmailAfterUndelete(List<Contact>
newRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
if(String.isBlank(con.Email) == false){ //
[email protected]
emailSet.add(con.Email); //
[email protected]
}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record

ls
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);

oo
checkDuplicate(newRecords, emailToContactMap);
}

public static void


ch
preventDuplicateContactBasedOnEmailBeforeUpdate(List<Contact>
newRecords, Map<Id, Contact> oldRecords){
// Check the duplicate records based on Email
/*
rS
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
* 2. Check if the record found
* 3. Add Error
he

*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
// Check if the Email is Changed
nt

Contact oldRecord = oldRecords.get(con.Id);


// add the email to set
// [email protected] --> Old Email
// [email protected] --> New/Updated Email
Pa

if(oldRecord.Email <> con.Email && String.isBlank(con.Email)


== false){ // [email protected]
emailSet.add(con.Email); //
[email protected]
} else {
con.Email.addError('Email can not be blank!');
}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
// Email+Company
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}

public static void preventDuplicateContactBasedOnEmail(List<Contact>


newRecords){
// Check the duplicate records based on Email

ls
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email

oo
* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
ch
for(Contact con : newRecords){
// add the email to set

if(String.isBlank(con.Email) == false){
rS
// null, ''
emailSet.add(con.Email);
} else {
con.Email.addError('Email can not be blank!');
he

}
}

Map<String, Contact> emailToContactMap = new Map<String,


nt

Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet]){
emailToContactMap.put(con.Email, con);
Pa

checkDuplicate(newRecords, emailToContactMap);
// keyset() - Set of Keys
// values() - List of values
// get(key) -
// #1 - [email protected]
// does email found - No
// Add a new entry in the Map
// #2 - [email protected]
// does email found - Yes
}
public static void checkDuplicate(List<Contact> newRecords,
Map<String, Contact> emailToContactMap){
for(Contact con : newRecords ){
// #3 - Recommended
// String key = Email+Company
if(emailToContactMap.containsKey(con.Email)){ //
[email protected]
con.addError('Duplicate contact found! '+con.Email);
con.Email.addError('Duplicate contact found!
'+con.Email);

ls
} else {
emailToContactMap.put(con.Email, con);
}

oo
}
}

public static void handleAfterInsert(List<Contact> newRecords){

}
ch
countContacts(newRecords);

public static void handleAfterUndelete(List<Contact> newRecords){


rS
countContacts(newRecords);
}

public static void handleAfterDelete(List<Contact> oldRecords){


he

countContacts(oldRecords);
}

public static void handleAfterUpdate(List<Contact> newRecords){


nt

countContacts(newRecords);
}

public static void countContacts(List<Contact> contactList){


Pa

Set<Id> accountIdsSet = new Set<Id>();


for(Contact con: contactList){
if(con.AccountId != null){
accountIdsSet.add(con.AccountId);
}
}
List<AggregateResult> agrslt = [SELECT count(Id), count(Email),
AccountId FROM Contact WHERE AccountId IN:accountIdsSet Group By
AccountId];
List<Account> accList = new List<Account>();
for(AggregateResult agr: agrslt){
Integer totalContact = (Integer)agr.get('expr0');
//agr.get('expr1');
Id accountId = (Id)agr.get('AccountId');
Account acc = new Account(Id = accountId,
Number_of_Contacts__c = totalContact);
accList.add(acc);
}
update accList;
}

ls
Apex Trigger

oo
trigger ContactTrigger on Contact (before insert, after insert, before
update, after update, after undelete, after delete) {
ch
/** US-8783 : Added by Amit Singh on 18-06-2024, to prevent
duplicate records based on Email */
if(trigger.isBefore && Trigger.isInsert){
rS

ContactTriggerHelper.preventDuplicateContactBasedOnEmail(Trigger.New);
} else if(Trigger.isBefore && Trigger.isUpdate){
he

ContactTriggerHelper.preventDuplicateContactBasedOnEmailBeforeUpdate(Tri
gger.New, (Map<Id, Contact>)Trigger.oldMap );
} else if(Trigger.isAfter && Trigger.isUpdate){
nt

ContactTriggerHelper.preventDuplicateContactBasedOnEmailBeforeUpdate(Tri
gger.New, (Map<Id, Contact>)Trigger.oldMap );
} else if(Trigger.isAfter && Trigger.isUndelete){
Pa

ContactTriggerHelper.preventDuplicateContactBasedOnEmailAfterUndelete(Tr
igger.New);
ContactTriggerHelper.handleAfterUndelete(Trigger.New);
} else if(Trigger.isAfter && Trigger.isInsert){
ContactTriggerHelper.handleAfterInsert(Trigger.New);
} else if(Trigger.isAfter && Trigger.isDelete){
ContactTriggerHelper.handleAfterDelete(Trigger.old);
} else if(Trigger.isAfter && Trigger.isUpdate){
ContactTriggerHelper.handleAfterUpdate(Trigger.New);
}
}
Day 34 - Rollup Summary Apex Trigger in Salesforce Part 2

Recording Link
https://youtu.be/Rg9jXVRiS70

Apex Trigger Scenarios


https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing

Prerequisite

ls
Create a Custom Field on the Account Object “Number of Contacts” with a
Number Data Type that does not have any decimal point in it.

oo
Requirement (Roll-up Summary Trigger)

Develop a Solution that will count the related contact related to the Account and
ch
store the information in the “Number of Contacts” field.
rS
Note:- The contact can be created/deleted/undeleted and updated from the
Account Record. So please keep that in mind

Questions to be Asked?
he

Question?
nt

● Which Object
○ Contact
● Events
Pa

○ after insert, after delete, after undelete, after update


● Functionality
○ Develop a Solution that will count the related contact related to the
Account and store the information in the “Number of Contacts” field.

Apex Class using Map

public class ContactTriggerHelper {

public static void


preventDuplicateContactBasedOnEmailAfterUndelete(List<Contact>
newRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){

ls
if(String.isBlank(con.Email) == false){ //
[email protected]
emailSet.add(con.Email); //

oo
[email protected]
}
}
// 003dL000001iwXZQAY
ch
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
rS
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
he

public static void


preventDuplicateContactBasedOnEmailBeforeUpdate(List<Contact>
nt

newRecords, Map<Id, Contact> oldRecords){


// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
Pa

* 1. Get the Existing Records of Contact Object based on Email


* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
// Check if the Email is Changed
Contact oldRecord = oldRecords.get(con.Id);
// add the email to set
// [email protected] --> Old Email
// [email protected] --> New/Updated Email
if(oldRecord.Email <> con.Email && String.isBlank(con.Email)
== false){ // [email protected]
emailSet.add(con.Email); //
[email protected]
} else if(String.isBlank(con.Email)) {
con.Email.addError('Email can not be blank!');
}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE

ls
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
// Email+Company
emailToContactMap.put(con.Email, con);

oo
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}
ch
public static void preventDuplicateContactBasedOnEmail(List<Contact>
newRecords){
// Check the duplicate records based on Email
rS
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
* 2. Check if the record found
he

* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
nt

// add the email to set

if(String.isBlank(con.Email) == false){
// null, ''
Pa

emailSet.add(con.Email);
} else {
con.Email.addError('Email can not be blank!');
}
}

Map<String, Contact> emailToContactMap = new Map<String,


Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet]){
emailToContactMap.put(con.Email, con);
}
checkDuplicate(newRecords, emailToContactMap);
// keyset() - Set of Keys
// values() - List of values
// get(key) -
// #1 - [email protected]
// does email found - No
// Add a new entry in the Map
// #2 - [email protected]
// does email found - Yes
}

ls
public static void checkDuplicate(List<Contact> newRecords,
Map<String, Contact> emailToContactMap){

oo
for(Contact con : newRecords ){
// #3 - Recommended
// String key = Email+Company
if(emailToContactMap.containsKey(con.Email)){ //
ch
[email protected]
con.addError('Duplicate contact found! '+con.Email);
con.Email.addError('Duplicate contact found!
'+con.Email);
rS
} else {
emailToContactMap.put(con.Email, con);
}
}
he

public static void handleAfterInsert(List<Contact> newRecords){


countContacts(newRecords);
nt

public static void handleAfterUndelete(List<Contact> newRecords){


countContacts(newRecords);
Pa

public static void handleAfterDelete(List<Contact> oldRecords){


countContacts(oldRecords);
}

public static void handleAfterUpdate(List<Contact> newRecords,


Map<Id, Contact> oldRecordMap){
Map<Id, Account> accountMap = new Map<Id, Account>();
for(Contact con : newRecords){
Contact oldRecord = oldRecordMap.get(con.Id);
if(oldRecord.AccountId <> con.AccountId && con.AccountId !=
null){
accountMap.put(con.AccountId, new Account(Id =
con.AccountId, Number_of_Contacts__c = 0) );
if(oldRecord.AccountId <> null){
accountMap.put(oldRecord.AccountId, new Account(Id =
oldRecord.AccountId, Number_of_Contacts__c = 0) );
}
}
}
System.debug(accountMap);
List<AggregateResult> agrslt = [SELECT count(Id) totalCount,

ls
count(Email) totalEmailCount, AccountId
FROM Contact
WHERE AccountId IN:

oo
accountMap.keySet() Group By AccountId];

for(AggregateResult agr: agrslt){

ch Integer totalContact = (Integer)agr.get('totalCount');


Id accountId = (Id)agr.get('AccountId');
Account acc = accountMap.get(accountId);
acc.Number_of_Contacts__c = totalContact;
rS
accountMap.put(accountId, acc );

}
he

update accountMap.values();
}

public static void countContacts(List<Contact> contactList){


nt

Map<Id, Account> accountMap = new Map<Id, Account>();


for(Contact con: contactList){
if(con.AccountId != null){
Pa

accountMap.put(con.AccountId, new Account(Id =


con.AccountId, Number_of_Contacts__c = 0) );
}
}

System.debug(accountMap);
List<AggregateResult> agrslt = [SELECT count(Id), count(Email),
AccountId
FROM Contact
WHERE AccountId IN:
accountMap.keySet() Group By AccountId];
for(AggregateResult agr: agrslt){

Integer totalContact = (Integer)agr.get('expr0');


Id accountId = (Id)agr.get('AccountId');
Account acc = accountMap.get(accountId);
acc.Number_of_Contacts__c = totalContact;
accountMap.put(accountId, acc );

update accountMap.values(); // List<Account>

ls
}

oo
}

Apex Class Using Set


ch
public class ContactTriggerHelper {

public static void


preventDuplicateContactBasedOnEmailAfterUndelete(List<Contact>
rS
newRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
he

* 1. Get the Existing Records of Contact Object based on Email


* 2. Check if the record found
* 3. Add Error
*/
nt

Set<String> emailSet = new Set<String>();


for(Contact con : newRecords){
if(String.isBlank(con.Email) == false){ //
[email protected]
Pa

emailSet.add(con.Email); //
[email protected]
}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}

public static void


preventDuplicateContactBasedOnEmailBeforeUpdate(List<Contact>
newRecords, Map<Id, Contact> oldRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
* 2. Check if the record found

ls
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();

oo
for(Contact con : newRecords){
// Check if the Email is Changed
Contact oldRecord = oldRecords.get(con.Id);
// add the email to set
ch // [email protected] --> Old Email
// [email protected] --> New/Updated Email
if(oldRecord.Email <> con.Email && String.isBlank(con.Email)
== false){ // [email protected]
rS
emailSet.add(con.Email); //
[email protected]
} else {
con.Email.addError('Email can not be blank!');
he

}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
nt

Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE
Email IN: emailSet AND ID NOT IN:newRecords ]){ // same record
// Email+Company
Pa

emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}

public static void preventDuplicateContactBasedOnEmail(List<Contact>


newRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on Email
* 2. Check if the record found
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
// add the email to set

if(String.isBlank(con.Email) == false){
// null, ''
emailSet.add(con.Email);
} else {

ls
con.Email.addError('Email can not be blank!');
}
}

oo
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact WHERE

}
ch
Email IN: emailSet]){
emailToContactMap.put(con.Email, con);
rS
checkDuplicate(newRecords, emailToContactMap);
// keyset() - Set of Keys
// values() - List of values
// get(key) -
he

// #1 - [email protected]
// does email found - No
// Add a new entry in the Map
// #2 - [email protected]
nt

// does email found - Yes


}

public static void checkDuplicate(List<Contact> newRecords,


Pa

Map<String, Contact> emailToContactMap){


for(Contact con : newRecords ){
// #3 - Recommended
// String key = Email+Company
if(emailToContactMap.containsKey(con.Email)){ //
[email protected]
con.addError('Duplicate contact found! '+con.Email);
con.Email.addError('Duplicate contact found!
'+con.Email);
} else {
emailToContactMap.put(con.Email, con);
}
}
}

public static void handleAfterInsert(List<Contact> newRecords){


countContacts(newRecords);
}

public static void handleAfterUndelete(List<Contact> newRecords){


countContacts(newRecords);
}

ls
public static void handleAfterDelete(List<Contact> oldRecords){
countContacts(oldRecords);
}

oo
public static void handleAfterUpdate(List<Contact> newRecords){
countContacts(newRecords);
}
ch
public static void countContacts(List<Contact> contactList){
//Set<Id> accountIdsSet = new Set<Id>();
Map<Id, Account> accountMap = new Map<Id, Account>();
rS
for(Contact con: contactList){
if(con.AccountId != null){
//accountIdsSet.add(con.AccountId);
accountMap.put(con.AccountId, new Account(Id =
he

con.AccountId, Number_of_Contacts__c = 0) );
}
}
// .keySet() - Set<Id>
nt

System.debug(accountMap);
List<AggregateResult> agrslt = [SELECT count(Id), count(Email),
AccountId
FROM Contact
Pa

WHERE AccountId IN:


accountMap.keySet() Group By AccountId];
// 001dL000001rKZXQA1 - XYZ
//List<Account> accList = new List<Account>();
for(AggregateResult agr: agrslt){

Integer totalContact = (Integer)agr.get('expr0');


Id accountId = (Id)agr.get('AccountId');
Account acc = accountMap.get(accountId);
acc.Number_of_Contacts__c = totalContact;
accountMap.put(accountId, acc );
//Account acc = new Account(Id = accountId,
Number_of_Contacts__c = totalContact);
//accList.add(acc);
// Approach #1 for delete operation
//accountIdsSet.remove(accountId);
}
/*if(accountIdsSet.size()>0){
for(Id recordId: accountIdsSet){
Account acc = new Account(Id = recordId,
Number_of_Contacts__c = 0);
accList.add(acc);

ls
}
}*/
update accountMap.values(); // List<Account>

oo
}

}
ch
Apex Trigger
rS
trigger ContactTrigger on Contact (before insert, after insert, before
update, after update, after undelete, after delete) {

/** US-8783 : Added by Amit Singh on 18-06-2024, to prevent


he

duplicate records based on Email */


if(trigger.isBefore && Trigger.isInsert){

ContactTriggerHelper.preventDuplicateContactBasedOnEmail(Trigger.New);
nt

} else if(Trigger.isBefore && Trigger.isUpdate){

ContactTriggerHelper.preventDuplicateContactBasedOnEmailBeforeUpdate(Tri
gger.New, (Map<Id, Contact>)Trigger.oldMap );
Pa

} else if(Trigger.isAfter && Trigger.isUpdate){

ContactTriggerHelper.preventDuplicateContactBasedOnEmailBeforeUpdate(Tri
gger.New, (Map<Id, Contact>)Trigger.oldMap );
ContactTriggerHelper.handleAfterUpdate(Trigger.New, (Map<Id,
Contact>)Trigger.oldMap);

} else if(Trigger.isAfter && Trigger.isUndelete){

ContactTriggerHelper.preventDuplicateContactBasedOnEmailAfterUndelete(Tr
igger.New);
ContactTriggerHelper.handleAfterUndelete(Trigger.New);
} else if(Trigger.isAfter && Trigger.isInsert){
ContactTriggerHelper.handleAfterInsert(Trigger.New);
} else if(Trigger.isAfter && Trigger.isDelete){
ContactTriggerHelper.handleAfterDelete(Trigger.old);
} else if(Trigger.isAfter && Trigger.isUpdate){

}
}

ls
Assignment

oo
Prerequisite
Create the Custom Fields on the Account Object “Sum of Closed Opportunity
Amount” & “Sum of Open Opportunity Amount” with a Currency Data Type with 2
ch
Decimal Points.

Requirement (Rollup Summary Trigger)


rS

Develop an Apex trigger to SUM the Opportunity Amount of the Closed Opportunity
and Open Opportunities. The closed opportunities are the ones with the Status
he

equal to “Closed Won” OR “Closed Lost”. And store the result in the “Sum of
Closed Opportunity Amount” & “Sum of Open Opportunity Amount” fields
respectively in the related Account for the opportunity.
nt

Note:- The Opportunity can be created/deleted/undeleted and updated from the


Account Record. So please keep that in mind
Pa

Scenarios for Update DML

● The account has been changed


● The amount has been changed

Resources

● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_triggers.htm
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers
● https://trailhead.salesforce.com/content/learn/projects/quick-start-apex-co
ding-for-admins/create-a-trigger
● https://trailhead.salesforce.com/content/learn/modules/apex_testing/apex_t
esting_triggers
● https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_t
riggers_bulk

ls
Day 35 - How to Send an Email from Apex

oo
Recording Link
https://youtu.be/vQVGI9Qo_ jc

Apex Trigger Scenarios


ch
https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing
rS

Scenario
he

Create an Apex trigger on the Lead object and check if either the Email and Phone
is blank then Send the Email to Lead Owner.
nt

The Email Content should look like below

Hi User,
Pa

Important Information either Lead Email or Lead Phone is missing.

Click Here to visit the lead.

Please take the proper action ASAP

Thanks & Regards,


Your Company Information
Code used into the Apex Class

LeadTriggerHandler Class

public class LeadTriggerHandler {

public static void sendEmail(List<Lead> newRecords){


List<Messaging.SingleEmailMessage> emails = new

ls
List<Messaging.SingleEmailMessage>();
for(Lead lead: newRecords){

oo
/** Check if the Lead Email OR Phone is Blank */
if(String.isBlank(lead.Email) || String.isBlank(lead.Phone)){
Messaging.SingleEmailMessage email = new
ch
Messaging.SingleEmailMessage(); // Created a blank Email
email.setSubject('Important Information is missing for the lead
'+lead.FirstName ?? '' +' '+Lead.LastName);
rS

String emailBody = 'Hi User, <br/><br/> Important Information either


Lead Email or lead Phone is missing.';
he

//
https://learning-koala-dev-ed.develop.lightning.force.com/lightning/r/Lead/00QdL
nt

000003zZwpUAE/view
String baseUrl = System.URL.getOrgDomainUrl().toExternalForm();
baseUrl += '/lightning/r/Lead/'+lead.Id+'/view';
Pa

emailBody += '<br/><br/> <a href="'+baseUrl+'" target="_blank">Click


Here</a> to visit the lead.<br/><br/> ';
emailBody += 'Please take the proper action ASAP <br/><br/> Thanks
& Regards,<br/> Your Company Information';

email.setHtmlBody(emailBody);
List<String> toAddress = new List<String>();
toAddress.add(lead.OwnerId);
email.setToAddresses(toAddress);
email.setBccAddresses( new List<String>{
'[email protected]' } );
email.setSenderDisplayName('[email protected]');
emails.add(email);
}
}
List<Messaging.SendEmailResult> results = Messaging.sendEmail( emails );
for(Messaging.SendEmailResult sr : results){

ls
if(sr.isSuccess()){
System.debug('Email Sent!');

oo
} else {

}
}ch
}
rS

Dispatcher Class
he

public class LeadTriggerDispatcher {

public static void run(System.TriggerOperation operationType){


nt

switch on operationType {
WHEN AFTER_INSERT {
LeadTriggerHandler.sendEmail(Trigger.New);
Pa

}
}
}
}
Lead Trigger

trigger LeadTrigger on Lead (after insert) {


LeadTriggerDispatcher.run(Trigger.operationType);
}

ls
Resources

oo
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_url.htm#apex_System_URL_methods
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
ch
_classes_email_outbound_messaging.htm#apex_System_Messaging_sendEm
ail
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
rS

_classes_email_outbound_single.htm#apex_Messaging_SingleEmailMessage_
setToAddresses
● https://automationchampion.com/2022/03/09/how-to-setup-dkim-key-in-sa
he

lesforce-2/
● https://automationchampion.com/2022/03/09/how-to-setup-sender-policy-f
ramework-spf-for-salesforce/
nt

Day 36 - Work with Recursive Apex Trigger in Salesforce


Pa

Recording Link
https://youtu.be/Q8wykmRCeZQ

Apex Trigger Scenarios


https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing
What is recursive Apex Trigger

In Salesforce, a recursive trigger refers to a situation where a trigger


inadvertently calls itself, leading to potential infinite loops and performance
issues. Salesforce triggers are often used to automate business logic, but
without proper handling, they can end up calling themselves recursively.

Managing Recursive Triggers in Salesforce

To manage recursive triggers effectively in Salesforce, you can implement

ls
several strategies:

oo
1. Static Boolean Variable:
○ Use a static Boolean variable to prevent the trigger from
running multiple times in the same context.
ch
2. Static Set or List:
○ Use a static set or list to keep track of processed records to
avoid reprocessing them.
rS
3. Trigger Context Variables:
○ Utilise trigger context variables like Trigger.isBefore,
Trigger.isAfter, Trigger.isInsert, Trigger.isUpdate, etc., to control
he

when the trigger should execute its logic.

Example of Preventing Recursive Triggers


nt

Here’s an example using a static Boolean variable to prevent recursion in a


Salesforce trigger:
Pa

● Create an Apex Class to hold the static variable:

public class TriggerHelper {


public static Boolean isTriggerRunning = false;
}

● Modify the Trigger to Use the Static Variable:


trigger AccountTrigger on Account (before insert, before update, after
insert, after update) {
if (TriggerHelper.isTriggerRunning) {
return; // Exit the trigger to prevent recursion
}

try {
// Set the static variable to true to indicate the trigger is
running
TriggerHelper.isTriggerRunning = true;

ls
// Trigger logic here
if (Trigger.isBefore && Trigger.isInsert) {
for (Account acc : Trigger.new) {

oo
// Your before insert logic
}
}

ch
if (Trigger.isBefore && Trigger.isUpdate) {
for (Account acc : Trigger.new) {
// Your before update logic
}
rS
}

if (Trigger.isAfter && Trigger.isInsert) {


for (Account acc : Trigger.new) {
he

// Your after insert logic


}
}
nt

if (Trigger.isAfter && Trigger.isUpdate) {


for (Account acc : Trigger.new) {
// Your after update logic
}
Pa

}
} finally {
// Reset the static variable to false after trigger execution
TriggerHelper.isTriggerRunning = false;
}
}
Explanation:

● Static Variable: TriggerHelper.isTriggerRunning is a static Boolean


variable used to track whether the trigger is currently running. Since
static variables retain their values across trigger contexts, this
variable helps prevent the trigger from re-entering.
● Trigger Logic: The actual logic of the trigger is placed within the try
block, ensuring that the static variable is reset to false in the finally
block, even if an exception occurs.

Other Considerations

ls
● Bulk Processing: Ensure your trigger is bulk-safe by processing
records in bulk rather than one at a time.

oo
● Governor Limits: Be mindful of Salesforce governor limits. Use SOQL
and DML operations efficiently to avoid hitting these limits.
● Testing: Thoroughly test your trigger logic, including scenarios where
recursion might occur, to ensure that your static variable approach
ch
effectively prevents recursion.
rS

CaseTrigger
Insert CaseRecord
Trigger
he

Insert case record

Case Record Inserted → Case trigger executed → Asset Object Record → Asset
nt

Trigger → Updates Account Object → Account Trigger → Cae Record which was
inserted → Case Update Trigger was executed
Pa
Code Used in the Apex Class

Apex Trigger

trigger CaseTrigger on Case (after insert) {


/* Check if the trigger is already executed with the status boolean
variable */
if(CaseTriggerHandler.runOnce){ // false
return;
}

ls
List<Case> caseList = new List<Case>();

for(Case c: Trigger.New){ // 200

oo
Case newRecord = new Case();
newRecord.Subject = c.Subject;
newRecord.Description = c.Description;
newRecord.Status = c.Status;
ch
newRecord.Origin
newRecord.Priority
= c.Origin;
= c.Priority;
newRecord.ParentId = c.Id;
newRecord.ContactId = c.ContactId;
rS
caseList.add(newRecord);
//CaseTriggerHandler.runOnce = True;
//insert newRecord; // Do not make DML use the List
}
he

CaseTriggerHandler.runOnce = True;
insert caseList; // 200 records are inserted

// Trigger - 200 chunks


nt

// Scenario #1 - 190 records are inserted


// Scenario #2 - 210 records are inserted
Pa

/*
1. 200
2. 10 -->
Flag is set to True
Logic is not executed even for the 1st time
*/
/*
Trigger Framework
*/
}
Handler Class

public class CaseTriggerHandler {


public static Boolean runOnce = false;
}

Anonymous Window Code

List<Case> caseList = new List<Case>();

ls
for(Integer i=0; i< 250; i++){
Case newRecord = new Case();
newRecord.Subject = 'c.Subject';

oo
newRecord.Description = 'c.Description';
newRecord.Status = 'New';
newRecord.Origin = 'Email';
newRecord.Priority = 'Low';
ch
newRecord.ContactId
caseList.add(newRecord);
= '003dL000000xSMtQAM';

}
rS
insert caseList;

// 250
// 200 -->
he

// static variable as true


// 200 child records are inserted
// trigger again executed for these new 200 records
// true
nt

// return
// 50 records trigger will execute
// true
// return
Pa
Day 37 - How files are being managed by @salesforce - Data Model with
@sfdcpanther

Recording Link
https://youtu.be/GAU6Ilo4UDY

Apex Trigger Scenarios


https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing

Link to the File We used in the Class to understand the structure of the file

ls
https://learning-koala-dev-ed.develop.my.salesforce.com/sfc/p/dL000000j3kv/a/dL
0000000rE1/68GSqrlruSXqdKjg_gvW77xNkdccmhdmAZ7SEucgB1k

oo
Apex Code
ch
ContentVersion version = new ContentVersion();
version.Title = 'AmitSingh.txt';
rS
version.PathOnClient = 'AmitSingh.txt';
version.VersionData = Blob.valueOf('This is a simple text');
insert version;
he

Resources

● https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/obj
nt

ect_reference/sforce_api_objects_contentdistribution.htm
● https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/obj
ect_reference/sforce_api_objects_contentversion.htm
Pa

● https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/obj
ect_reference/sforce_api_objects_contentdocument.htm
● https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/obj
ect_reference/sforce_api_objects_contentdocumentlink.htm
Day 38 - Create the Apex Trigger to Copy the Files from Task,Event & Case to Related
Account Record

Recording Link
https://youtu.be/PO2dlUuWARw

Apex Trigger Scenarios


https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing

ls
Requirement

oo
Develop a solution so that whenever a file is getting inserted under Task, Event or
Case Object the same file should be linked to the related Account Record.

ch
Note: - The file should only get linked to the account record. If the task and event
records are associated with Account (which means the what id is of type Account)
then only the file should get copied.
rS

Hints

● Explore Content Document Link


he

● You can use the getSObjectType method to check the type of What Id
● Explore the clone method of the List class
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
nt

_methods_system_list.htm#apex_System_List_clone
● https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/obj
Pa

ect_reference/sforce_api_objects_contentdocumentlink.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_sobject.htm#apex_System_SObject_getSObjectType

Code Used in the Apex Class

Apex trigger
trigger ContentDocumentLinkTrigger on ContentDocumentLink (after insert)
{
// Task, Event, Case
Set<Id> taskIdsSet = new Set<Id>();
Set<Id> eventIdsSet = new Set<Id>();
Set<Id> caseIdsSet = new Set<Id>();
for(ContentDocumentLink link: Trigger.New){ // 3 files 1 for each
object ( Task, Event, Case )
Schema.SObjectType linkType =
link.LinkedEntityId.getSObjectType();
Schema.SObjectType taskType = Task.getSObjectType();

ls
if(linkType == taskType){
taskIdsSet.add(link.LinkedEntityId);
} else if(link.LinkedEntityId.getSObjectType() ==

oo
Event.getSObjectType()){
eventIdsSet.add(link.LinkedEntityId);
} else if(link.LinkedEntityId.getSObjectType() ==
Case.getSObjectType()){

}
}
ch caseIdsSet.add(link.LinkedEntityId);

// SOQL on Task Object


rS
if(taskIdsSet?.size() > 0){
List<Task> taskList = [SELECT Id, WhatId FROM Task WHERE Id
IN:taskIdsSet AND What.Type ='Account'];
if(taskList?.size() >0){
he

List<ContentDocumentLink> linksToInsert = new


List<ContentDocumentLink>();
// Insert ContentDocumentLink Record
for(ContentDocumentLink link: Trigger.New){ // 2
nt

// Case
// Task
for(Task t: taskList){ // 2
if(t.Id == link.LinkedEntityId){ // Task, Case,
Pa

Event, Account, Lead, Payment__c, Payment


// Prepare a ContentDocumentLink Record
ContentDocumentLink newLink = new
ContentDocumentLink();
newLink.ContentDocumentId =
link.ContentDocumentId;
newLink.LinkedEntityId = t.WhatId;
linksToInsert.add(newLink);
}
}
}
insert linksToInsert;
}
}

if(eventIdsSet?.size() > 0){


Map<String, String> eventIdToAccountIdMap = new Map<String,
String>();
for(Event evt: [SELECT Id, WhatId FROM Event WHERE Id
IN:eventIdsSet AND What.Type ='Account']){
eventIdToAccountIdMap.put(evt.Id, evt.WhatId);
}
List<ContentDocumentLink> linksToInsert = new

ls
List<ContentDocumentLink>();
for(ContentDocumentLink link: Trigger.New){
if(eventIdToAccountIdMap.containsKey(link.LinkedEntityId)){

oo
ContentDocumentLink newLink = new ContentDocumentLink();
newLink.ContentDocumentId = link.ContentDocumentId;
newLink.LinkedEntityId =
eventIdToAccountIdMap.get(link.LinkedEntityId);

}
ch }
linksToInsert.add(newLink);

insert linksToInsert;
rS
}
if(caseIdsSet?.size() > 0){

}
he

Anonymous Window Code


nt

ContentDocumentLink link = new ContentDocumentLink();


link.ContentDocumentId = '069dL000003FRsbQAG';
Pa

link.LinkedEntityId = '003dL000000GLS3QAO';
insert link;

ContentVersion version = new ContentVersion();


version.Title = 'Sample.txt';
version.PathOnClient = 'Sample.txt';
version.VersionData = Blob.valueOf('This is a simple text');
version.FirstPublishLocationId = '00TdL000000VteXUAS';
insert version;
Day 39 - Content version concept in Salesforce

Recording Link
https://youtu.be/4f25PqK4Gzc

Apex Trigger Scenarios


https://docs.google.com/document/d/1cFjKruYqLMMbyhguGfHh-iRxF9FECO9gCPxYJ
0GFoFc/edit?usp=sharing

Day 40 - Introduction to Lightning Flow in Salesforce

ls
Recording Link

oo
https://youtu.be/qmVvrmhjOis

Introduction
ch
Lightning Flow is a powerful automation tool within Salesforce that enables
users to automate business processes, enforce business logic, and collect
data from users within Salesforce without writing code. It consists of two
rS
main components: Flow Builder and Process Builder.

Key Components of Lightning Flow


he

1. Flow Builder:
○ Types of Flows:
nt

■ Screen Flows: Interactive flows that require user input.


■ Autolaunched Flows: Non-interactive flows that are
triggered automatically.
Pa

■ Scheduled Flows: Autolaunched flows that run on a


schedule.
■ Platform Event Trigger Flow
■ Record Trigger Flow Orchestration
○ Elements:
■ Interaction Elements: Screens, decisions, assignments.
■ Logic Elements: Decisions, loops, assignments.
■ Data Elements: Create records, update records, delete
records.
○ Resources: Variables, collections, formulas, choices.
2. Process Builder:
○ Automation: Allows the creation of automated processes
triggered by record changes or events.
○ Criteria: Define conditions that trigger actions.
○ Actions: Update records, create records, send emails, launch

ls
flows.

Building a Flow

oo
1. Define the Purpose: Understand the business process you aim to
automate.
ch
2. Design the Flow:
○ Identify the steps involved.
○ Determine the input and output required.
rS

3. Build the Flow:


○ Use Flow Builder to drag and drop elements.
○ Configure each element (e.g., screens for user input, decisions
he

for branching logic).


4. Test the Flow:
○ Validate the flow logic.
nt

○ Ensure it meets the business requirements.


5. Activate and Deploy:
Pa

○ Activate the flow to make it available for use.


○ Deploy it to the necessary environments.

Use Cases

1. Automating Lead Conversion: Automatically convert leads to


opportunities based on predefined criteria.
2. Onboarding Processes: Guide new employees through onboarding
steps with interactive screen flows.
3. Service Requests: Automate service request handling and updates.
4. Data Collection: Create interactive forms for data collection and
validation.

Benefits

● Efficiency: Reduces manual tasks and increases productivity.


● Consistency: Ensures consistent application of business rules.
● User Experience: Enhances user experience with guided processes.

ls
● Flexibility: Easily adapt to changing business requirements without
extensive code changes.

oo
Best Practices

● Keep It Simple: Start with simple flows and gradually add complexity.
ch
● Documentation: Document your flows for future reference and
maintenance.
rS
● Versioning: Maintain versions of your flows to track changes and roll
back if needed.
● Testing: Rigorously test flows in a sandbox environment before
deployment.
he
nt
Pa
Types of flow

ls
oo
Resources
ch
rS
● Flow Builder Help Document
● Salesforce Flow YouTube Playlist
he

DAY41 - Create your First Screenflow & Create Record Using Flow

Recording Link
nt

https://youtu.be/d-I2jQOHFVw

Scenarios for the Practices


Pa

Link to the Document

Requirements

1. Create a Screen Flow that will be used to display the “Thought of the Day”
using a Custom Label. Once the flow is developed and activated then add
the flow into the Home Page of the Sales Application.
ls
oo
ch
rS

2. Create a Screen Flow that will be used to create the Case Record. The
Screen flow will have the following inputs on the Case
he

a. Subject
b. Description
c. Status
nt

d. Case Origin
e. Contact Name
Pa
Resources

● Flow Builder Help Document


● Salesforce Flow YouTube Playlist

DAY42 - How to Handle the Errors in Flow & Display the Record Url in Screen

Recording Link
https://youtu.be/eSqTN_KWOKw

ls
Scenarios for the Practices

oo
Link to the Document

Resources



ch
Flow Builder Help Document
Salesforce Flow YouTube Playlist
rS
Formulas for your reference

For Getting the Base Url


he

LEFT({!$Api.Partner_Server_URL_610}, FIND( '/services',


{!$Api.Partner_Server_URL_610}))

For Error Message


nt

LEFT( {!$Flow.FaultMessage}, Find("You can look up ExceptionCode", {!$Flow.FaultMessage} )


-1)
Pa

DAY43 - How to Use If-Else, For Loop & Flow Inside Record Page, as a Quick Action
button

Recording Link
https://youtu.be/Q0Uqq-U3NrA

Scenarios for the Practices


Link to the Document

Formulas for your reference


For Getting the Base Url

LEFT({!$Api.Partner_Server_URL_610}, FIND( '/services',


{!$Api.Partner_Server_URL_610}))

For Error Message

LEFT( {!$Flow.FaultMessage}, Find("You can look up ExceptionCode", {!$Flow.FaultMessage} )


-1)

ls
oo
ch
rS
he

Resources

● Flow Builder Help Document


nt

● Salesforce Flow YouTube Playlist


Pa

DAY44 - Update records with for loop & Display the data in the form of DataTable in
Salesforce Flow

Recording Link
https://youtu.be/-BttMoMVARo

Scenarios for the Practices


Link to the Document

Scenario
Create a Screen flow that will be sitting on the Account Record and update the
Account Phone to all the related contact Phone in such a way that Account Phone
and Contact Phone should be the same. The screen flow will have multiple
screens.

● Screen 1 - Will display the confirmation message “Click Next to update


Contacts Record”
● Screen 2 - User should be presented with the list of all the records in a
datatable
● Screen 3 - Success Message.

Note:- Please have the proper fault paths for all the data elements wherever it’s

ls
required.

oo
ch
rS
he
nt

Resources

● Flow Builder Help Document


Pa

● Salesforce Flow YouTube Playlist

Day 45 - Record Triggered Flows

Recording Link
https://youtu.be/27haJjCg0TQ

Scenarios for the Practices


Link to the Document
Formulas for your reference

For Getting the Base Url

LEFT({!$Api.Partner_Server_URL_610}, FIND( '/services',


{!$Api.Partner_Server_URL_610}))

For Error Message

LEFT( {!$Flow.FaultMessage}, Find("You can look up ExceptionCode", {!$Flow.FaultMessage} )


-1)

ls
Introduction

Record Triggered flows are the automation process which executes

oo
automatically, when there is any DML happens in any object and these
types of triggers do not require any User Intervention.
ch
● Create
● Update
● Delete
rS

Global Variable

→ $Record [ All Fields ] ~= Trigger.New


he

→ $Record__Prior [ All Fields ] previous state in case of Update ~=


Trigger.Old
nt

Existing Account →
Pa

Name → PantherSchools.com

Updating account

Name → Academy.PantherSchools.com (latest value )

$Record → Name ⇒ Academy.PantherSchools.com

$Record__Prior → Name ⇒ PantherSchools.com


Example1 - Whenever an account is created do a Chatter Post on the
account detail page and below the message.

Trigger Types of Record triggered Flows

ls
oo
ch
● After Save Flow
rS
● Before Save Flow
● Async
● Record Trigger Orchestration
he
nt
Pa
When to Use which Record Trigger Flow

ls
oo
ch
rS

Formula Used for Chatter Post


he

The contact {!$Record.FirstName} {!$Record.LastName} has been created by


{!$Record.Owner.FirstName} {!$Record.Owner.LastName} on
nt

{!$Flow.CurrentDate}
Pa

Resources

● https://help.salesforce.com/s/articleView?id=sf.flow.htm&language=en
_US&type=5
● Flow Builder Help Document
● Salesforce Flow YouTube Playlist
DAY46 - Record Triggered Flow Continue….

Recording Link
https://youtu.be/N2QQQhnLDSA

Scenarios for the Practices


Link to the Document

Formulas for your reference

ls
For Getting the Base Url

LEFT({!$Api.Partner_Server_URL_610}, FIND( '/services',

oo
{!$Api.Partner_Server_URL_610}))

For Error Message


ch
LEFT( {!$Flow.FaultMessage}, Find("You can look up ExceptionCode", {!$Flow.FaultMessage} )
-1)

Requirement
rS

Create a Record Triggered flow so that when an Account is created and


either Industry or Phone is blank then create a follow up tak for that
he

Account with the following information.

1. Subject - Followup with the Account - {!$Record.Name}


2. Description/Comments - A new Account- {!$Record.Name} has been
nt

created. Please Follow Up with the Account and capture the missing
details.
Pa

3. Due Date - 3 Days from today ( current date + 3 days )


4. Related To(WhoId) - Account.Id
5. Status → New/Not Started
6. Priority → High

An error has occurred while creating the task record under the account
<Account Name> with the below error - <error message>
Chatter Message Formula

"An error has occurred while creating the task record under the account "+
{!$Record.Name} + " with the below error "+ {!$Flow.FaultMessage}

Additional Requirement

Also, send an email to the account owner with the following details

Dear <Account Owner>

ls
The account <Account Name> has been created without industry or Phone.
Could you please take the proper action and get the details populated.

oo
You can access the record by using <account name with link>.

Regards, ch
<Company Name>
rS
Email Template Body used in flow

Dear {!$Record.Owner.FirstName} {!$Record.Owner.LastName},


The account {!$Record.Name} has been created without Industry or Phone.
he

Could you please take the proper action and get the details populated.
You can access the record by using Link Here {!$Record.Name}
nt

Regards,
PantherSchools
Pa
Final Flow

ls
oo
ch
Resources
rS

● https://help.salesforce.com/s/articleView?id=sf.flow.htm&language=en
_US&type=5
● Flow Builder Help Document
he

● Salesforce Flow YouTube Playlist


nt
Pa
DAY47 - Send the Email Alert using Flow with Template or Email Alert

Recording Link
https://youtu.be/uG3vTbLCm1U

Scenarios for the Practices


Link to the Document

Formulas for your reference

ls
For Getting the Base Url

LEFT({!$Api.Partner_Server_URL_610}, FIND( '/services',

oo
{!$Api.Partner_Server_URL_610}))

For Error Message


ch
LEFT( {!$Flow.FaultMessage}, Find("You can look up ExceptionCode", {!$Flow.FaultMessage} )
-1)

Requirement
rS

Create a Record Triggered Flow That will send an email to the Opportunity Owner
& Primary Contact (Custom Lookup field on Opportunity ) whenever an Opportunity
he

is created in Salesforce and the Primary Contact Email is not Blank. The email
should be like below.
nt

Note:- The prerequisite is to Create a Custom Lookup field on Opportunity with


Contact and Label it as Primary Contact
Pa
Email Template Code

ls
oo
ch
rS
he
nt
Pa

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,
initial-scale=1.0">
<title>New Opportunity Notification</title>
<style>
@import
url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&dis
play=swap');
body {
font-family: 'Inter', sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #f7f9fc;
}
.container {
background-color: #ffffff;

ls
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);

oo
}
.header {
background-color: #34d399;
color: white;

}
ch
padding: 30px;
text-align: left;

.header h1 {
rS
margin: 0;
font-weight: 600;
font-size: 24px;
letter-spacing: -0.5px;
he

}
.content {
padding: 40px;
}
nt

.field {
margin-bottom: 20px;
display: flex;
align-items: baseline;
Pa

}
.label {
font-weight: 600;
color: #059669;
flex: 0 0 140px;
font-size: 14px;
}
.value {
flex: 1;
font-size: 16px;
color: #4b5563;
}
.footer {
text-align: center;
font-size: 12px;
color: #6b7280;
padding: 20px;
background-color: #f9fafb;
border-top: 1px solid #e5e7eb;
}
.cta-button {
display: inline-block;
background-color: #059669;

ls
color: white;
padding: 12px 24px;
text-decoration: none;

oo
border-radius: 6px;
margin-top: 30px;
font-weight: 600;
font-size: 14px;

}
ch transition: all 0.3s ease;
box-shadow: 0 4px 6px rgba(5, 150, 105, 0.25);

.cta-button:hover {
rS
background-color: #047857;
transform: translateY(-2px);
box-shadow: 0 6px 8px rgba(5, 150, 105, 0.3);
}
he

p {
color: #4b5563;
font-size: 16px;
margin-bottom: 20px;
nt

}
.highlight {
background-color: #d1fae5;
border-radius: 4px;
Pa

padding: 2px 6px;


font-weight: 600;
color: #059669;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>New Opportunity Created</h1>
</div>
<div class="content">
<p>Hello {!Opportunity.OwnerFullName},</p>
<p>A new opportunity has been created in Salesforce and
assigned to you. Here are the key details:</p>

<div class="field">
<span class="label">Opportunity Name:</span>
<span class="value">{!Opportunity.Name}</span>
</div>
<div class="field">
<span class="label">Account Name:</span>
<span class="value">{!Opportunity.Account}</span>

ls
</div>
<div class="field">
<span class="label">Amount:</span>

oo
<span class="value"><span
class="highlight">{!Opportunity.Amount}</span></span>
</div>
<div class="field">
ch <span class="label">Close Date:</span>
<span class="value">{!Opportunity.CloseDate}</span>
</div>
<div class="field">
rS
<span class="label">Stage:</span>
<span class="value">{!Opportunity.StageName}</span>
</div>
<div class="field">
he

<span class="label">Probability:</span>
<span class="value">{!Opportunity.Probability}%</span>
</div>
<div class="field">
nt

<span class="label">Type:</span>
<span class="value">{!Opportunity.Type}</span>
</div>
Pa

<p>Please review this opportunity and update it with any


additional information you may have.</p>

<a href="{!Opportunity.Link}" target="_blank"


class="cta-button">View Opportunity Details</a>
</div>
<div class="footer">
<p>This is an automated notification from your Salesforce
system. For assistance, please contact your system administrator.</p>
</div>
</div>
</body>
</html>

Resources

● https://help.salesforce.com/s/articleView?id=sf.flow.htm&language=en
_US&type=5
● Flow Builder Help Document
● Salesforce Flow YouTube Playlist

ls
DAY48 - How to Invoke Apex Class from flow & Auto Launched

oo
Flows

Recording Link
ch
https://youtu.be/P6n9LV0mGuo

Scenarios for the Practices


rS
Link to the Document

Formulas for your reference


he

For Getting the Base Url

LEFT({!$Api.Partner_Server_URL_610}, FIND( '/services',


{!$Api.Partner_Server_URL_610}))
nt

For Error Message

LEFT( {!$Flow.FaultMessage}, Find("You can look up ExceptionCode", {!$Flow.FaultMessage} )


Pa

-1)

Invocable Apex in Salesforce

is a powerful feature that allows you to call Apex methods directly from Flow,
Process Builder, and the REST API. Here's an overview of Invocable Apex:

1. Purpose:
○ Enables you to execute complex logic or operations that are difficult
or impossible to achieve using declarative tools alone.
○ Allows you to leverage the power of Apex within your flows and
processes.
2. Key Characteristics:
○ Invocable methods are annotated with @InvocableMethod.
○ They must be static and public.
○ They can accept either no parameters or a single parameter of type
List<SomeType>.
○ They can return void or a List<SomeType>.

ls
public class MyInvocableClass {
@InvocableMethod(label='Create or Update Accounts')

oo
public static List<Result> createOrUpdateAccounts(List<Request>
requests) {
List<Result> results = new List<Result>();

}
ch
return results;
rS
// Input class
public class Request {
@InvocableVariable(required=true label='Account Name'
description=’Provide the name of the Account’)
public String accountName;
he

// Output class
public class Result {
nt

@InvocableVariable(label='Account ID')
public Id accountId;
}
Pa

3. Input and Output:


○ Input and output parameters must be defined as Apex classes if
accepting multiple parameters
○ These classes should have properties annotated with
@InvocableVariable.
4. Usage in Flow:
○ In Flow Builder, you can use the "Apex Action" element to call your
invocable method.
○ Map flow variables to the input parameters of your Apex method.
○ Store the output in flow variables for further use.
5. Bulkification:
○ Invocable methods should be designed to handle bulk operations.
○ They receive and return lists, allowing for efficient processing of
multiple records.
6. Error Handling:

ls
○ Use try-catch blocks in your Apex to handle exceptions.
○ You can throw a custom exception (InvocableActionException) to

oo
display error messages in Flow.
7. Governor Limits:
○ Invocable Apex is subject to standard Apex governor limits.
ch
○ Be mindful of these limits, especially when processing large data
volumes.
8. Best Practices:
rS
○ Keep invocable methods focused on a specific task.
○ Use clear, descriptive labels for your methods.
○ Document the expected inputs and outputs.
○ Consider performance implications, especially for methods that might
he

be called in bulk.
9. Use Cases:
○ Complex calculations or data manipulations.
nt

○ Integration with external systems.


○ Operations requiring SOQL queries or DML that are too complex for
Pa

declarative tools.

DAY49 - Error Handling in Salesforce Flows with Subflows

Recording Link
https://youtu.be/NUblBoHfeiY

Scenarios for the Practices


Link to the Document
Formulas for your reference

For Getting the Base Url

LEFT({!$Api.Partner_Server_URL_610}, FIND( '/services',


{!$Api.Partner_Server_URL_610}))

For Error Message

LEFT( {!$Flow.FaultMessage}, Find("You can look up ExceptionCode", {!$Flow.FaultMessage} )


-1)

ls
Introduction to Subflow in Salesforce

oo
Subflows are a powerful feature in Salesforce Flow that allow you to modularize
and reuse portions of your flow logic. They're essentially flows that are called from
within another flow, known as the master flow or parent flow.
ch
Key points about Subflows:

1. Purpose:
rS

○ Reusability: Create common logic once and reuse it in multiple flows.


○ Simplification: Break complex flows into smaller, more manageable
pieces.
he

○ Maintenance: Update logic in one place that affects multiple flows.


2. How they work:
○ A subflow is a regular flow that is invoked by another flow.
nt

○ The parent flow passes variables to the subflow and can receive
variables back.
3. Benefits:
Pa

○ Improved organization and readability of complex processes.


○ Easier maintenance and updates.
○ Consistency across business processes.
4. Creating a Subflow:
○ Build a flow as you normally would.
○ Ensure it has clearly defined input and output variables.
5. Using a Subflow:
○ In the parent flow, add a "Subflow" element.
○ Select the flow you want to use as a subflow.
○ Map the variables between the parent flow and the subflow.
6. Best Practices:
○ Use descriptive names for subflows.
○ Document the purpose and expected inputs/outputs of each subflow.
○ Keep subflows focused on a specific task or logic.
7. Limitations:
○ Subflows count towards flow limits in your org.
○ There's a limit to how many levels deep you can nest subflows.
8. Use Cases:

ls
○ Common calculations used across multiple processes.
○ Standard approval or notification processes.

oo
○ Complex data operations that are reused in different contexts.
9. Debugging:
○ When debugging, you can step through subflows just like any other
ch element in your flow.

Subflows are an excellent way to create more efficient, maintainable, and scalable
automation in Salesforce. They allow you to build a library of reusable components
rS

that can significantly speed up development and ensure consistency across your
Salesforce org.
he

Flow Error handling

Create Error Log Object


nt

Create a Custom object [ Error log ] with the following fields

● Name ( Standard Auto Number field )


● Flow Name - Text (255)
Pa

● Flow Developer Name - Text (255)


● Flow Namespace - Text (255)
● Flow Interview ID - Text Area Long (1000)
● Interview Status - Text (255)
● InterviewDurationInMinutes - Number(6, 2)
● Flow Version Number - Number(3, 0)
● Error Title - Text (255)
● Error Content (Rich Text Area)
● Flow Node - Text Area (700)
● Flow Type - Picklist
○ Screen Flow
○ Record Triggered Flow
○ Auto Launched Flow
○ Platform Event Trigger
● Component Type (Picklist)
○ Flow

ls
○ Apex Trigger
○ Apex Class

oo
○ Batch Apex
○ LWC
○ Aura
ch
● Affected User - Lookup User
● Severity
○ ERROR
rS
○ WARNING
○ SUCCESS
○ INFORMATION
he

● Assigned User - Lookup User


● Resolution Comments - Text Area Long ( 5000 )
● Resolution Status - Picklist
nt

○ New
○ Assigned
Pa

○ In Progress
○ Need More Information
○ Fix Deployed
○ Out of Scope
○ Not a Bug
○ Not Reproducible
Here is the clear screenshots for your reference -
https://www.awesomescreenshot.com/image/49308636?key=13ea8ef0ec787
947634d86f1549a2064

ls
oo
ch
rS
he
nt
Pa

Here is the Link to the Sample Record -

https://www.awesomescreenshot.com/image/49308710?key=dee17a02a2970
e1da2f18338260f0a3c
ls
oo
ch
rS
he
nt
Pa

Resources

● https://help.salesforce.com/s/articleView?id=sf.flow.htm&language=en
_US&type=5
● Flow Builder Help Document
● Salesforce Flow YouTube Playlist
DAY50 - Database Methods vs DML methods

Recording Link
https://youtu.be/_mKBXCaCClk

Database Class DML Methods vs Normal DML

DML Statements

ls
1. Partial DML is not allowed. For example, if you have 100 records in the list,
then either all the records will be updated or none.

oo
2. You cannot get the list of successful and failed records.
3. Example - insert, update

Database Methods
ch
1. A partial DML is allowed. You can specify the Parameter in the Database
method as true or false, false to allow the partial update, and true for not
rS
allowing the same
2. You can get the list of successful and failed records as we have seen in the
example.
3. Example - Database.insert, Database.update
he
nt

Code that we have used in the Class

Contact con = new Contact(


Pa

Email = '[email protected]',
AccountId = '001dL000001rKZXQA2'
); // LastName

Contact con1 = new Contact(


Email = '[email protected]',
LastName = 'Singh'
); // Account

// 2 Log Records

List<Contact> conList = new List<Contact>{con, con1};


// Insert
// insert conList;
//insert(conList); // All Or None
List<Database.SaveResult> saveResultList = Database.insert(conList,
false);
/*for(Database.SaveResult sr: saveResultList){
if(!sr.isSuccess()){ // Error Condition
List<Database.Error> errors = sr.getErrors();
System.debug(' errors '+ errors );
System.debug(' errors size '+ errors.size());
String errorMessage = String.join(errors, ' ||:|| ');

ls
System.debug(errorMessage);

oo
}*/
List< ErrorLog__c > logs = new List< ErrorLog__c >();
for(Integer i=0; i< saveResultList.size(); i++){
Database.SaveResult sr = saveResultList.get(i);
ch
if(!sr.isSuccess()){ // Error Condition
Contact con = conList.get(i);
System.debug( JSON.serialize(con) );
List<Database.Error> errors = sr.getErrors();
rS
System.debug(' errors '+ errors );
System.debug(' errors size '+ errors.size());
String errorMessage = String.join(errors, ' ||:|| ');
System.debug(errorMessage);
he

System.debug('---------------');
ErrorLog__c log = new ErrorLog__c();
log.ApexClassName__c = 'AnynomousWindow';
log.ApexMethodName__c = 'AnynomousWindow';
nt

log.AffectedUser__c = UserInfo.getUserId();
log.ErrorTitle__c = 'Error While Creating Contact';
log.ErrorContent__c = errorMessage;
log.Payload__c = JSON.serialize(con);
Pa

log.Severity__c = 'ERROR';
log.ComponentType__c = 'Apex Class';
logs.add(log);
}
}
insert logs;

Resources

● Database Class
● SaveResult Class
● Error Class

Day 51 - Introduction to Test Class (Unit Test Cases) - By


Developer

Recording Link
https://youtu.be/QSsJCO6rT80

ls
Introduction

oo
In Salesforce, a test class is used to test the functionality of Apex classes and
triggers. The purpose of writing a test class is to ensure that your code works as
expected and that it meets the requirements of your business logic.
ch
Test classes help you to catch errors before they occur in production, so it is
crucial to write good test classes.
rS

● It's important to note that Salesforce requires a minimum of 75% code


coverage for all Apex classes and triggers.
● This means that at least 75% of the lines of code in your classes and
he

triggers must be covered by test methods.

Benefits of Test Classes in Salesforce


nt

● Ensuring code quality: Test classes help to ensure that your code works as
intended and meets the requirements of your business logic.
Pa

○ Positive test Cases


○ Negative Test Cases
○ Code Coverage
○ Bulk Record Testing
● Catching errors early: Test classes help to catch errors early in the
development process before they reach production. This saves time and
reduces the risk of costly errors that can impact business processes.
○ Resources Required
■ Time
■ Man Power
■ $
○ 1 Hour of time spent on development
○ Development Phase - Immediately fix ( 2 times ) - 2*4
○ Testing Phase - 4 times - 4*4
○ UAT (User Acceptance Testing) - 8 times - 8*4
○ System Integration Phase - 25 times - 25*5
○ Production - 100 times - 100*4
● Meeting code coverage requirements: Test classes are required in

ls
Salesforce to meet code coverage requirements.
● Facilitating collaboration: Test classes can help facilitate collaboration

oo
between developers and QA teams.
● Facilitating code maintenance: Test classes can help with code
maintenance by providing a way to test changes made to existing code.
ch
In summary, test classes are an essential part of the development process in
Salesforce. They help to ensure code quality, catch errors early, meet code
coverage requirements, facilitate collaboration, and facilitate code maintenance.
rS

IMPORTANT POINTS
he

● @IsTest must be used at the top of your Apex Class (Test Class)
● @IsTest shall be used at the top of your test method OR testMethod
nt

keyword shall be used while creating the test method


● Each test class must need to have at least 1 test method
Pa

Code we have used in Class

/*
Description: Test Class for AccountTrigger (Apex Trigger)
*/
@IsTest
public class AccountTriggerTest {

@IsTest
public static void handleBeforeInsertTest(){
Account acc = new Account(
Name = 'Salesforce.com From Test Class',
Rating = 'Hot'
);
insert acc;
}

@IsTest
public static void handleBeforeUpdateTest(){
Account acc = new Account(
Name = 'Salesforce.com From Test Class',

ls
Rating = 'Hot'
);
insert acc;

oo
acc.Rating__c = 'Hot';
update acc;
}
ch
@IsTest
public static void handleAfterDeleteTest(){
Account acc = new Account(
rS
Name = 'Salesforce.com From Test Class',
Rating = 'Hot'
);
insert acc;
he

delete acc;
}

@IsTest
nt

public static void handleAfterInsertTest(){


Account acc = new Account(
Name = 'Salesforce.com From Test Class',
Rating = 'Hot'
Pa

);
insert acc; /* Before Insert / after Insert */
System.debug('acc.OwnerId '+acc.OwnerId);
System.debug(' acc.Id '+acc.Id);
List<Account> accList = new List<Account>();
accList.add(acc);

List<Account> newRecords = [SELECT Id, Name, OwnerId FROM


Account WHERE Id =: acc.Id];
AccountTriggerHandler.handleAfterInsert(newRecords);

}
}

Day 52 - Test Setup Method, Test.startTest & Test.stopTest


method in Test Class

Recording Link
https://youtu.be/bewsa2WWqwY

ls
oo
Important Annotation of Test Class

● @isTest - If we use this annotation at the top of Apex Class then the class
will act as Test Class AND - If we use this annotation at the top of Apex
ch
method then the class will act as Test method inside the Test class.
● @testSetup - This method always gets executed before any test method is
executed. This method is used to prepare all the data that is required for
rS

the complete test class.


○ We used to create the common data that is required across all the
test cases inside that test class
he

● Test.startTest() & Test.stopTest() - These methods are very important as we


will always test our code within these two methods. These methods provide
a Fresh set of governor limits for the execution of our business logic.
nt

○ Apex Governor Limits

Code Used in the Practice Scenario


Pa

/*
Description: Test Class for AccountTrigger (Apex Trigger)
*/
@IsTest
public class AccountTriggerTest {

@TestSetup
public static void setupData(){
System.debug('setupData ');
Account acc = new Account(
Name = 'Salesforce.com From Test Class',
Rating = 'Hot',
Description = 'This is a test description'
);
insert acc;
}

@IsTest
public static void handleBeforeInsertTest(){
System.debug('Test ');
}

ls
@IsTest
public static void handleBeforeUpdateTest(){

oo
List<Account> accountList = [SELECT Id, Name, Phone FROM Account
WHERE Name = 'Salesforce.com From Test Class'];
// Call By Refence
System.debug(' accountList size '+ accountList.size() );

0
ch
//Account acc = accountList.get(0); // List index out of bound :

for(Account acc: accountList){


acc.Rating__c = 'Hot';
rS
}
/*
1. 150 DML
2. 100 SOQL
he

3. CPU - 10 seconds for Synchronous & 60 for Async


4. 6 MB for Sync - 12 MB for Async
5. 10 Email for Dev Org
*/
nt

// Fresh Set of Governor Limits

// 99 - SOQL
// 100
Pa

// 101 - Error
Test.startTest(); // Start Test
update accountList; // Governor Limits will be reset
Test.stopTest();
}

@IsTest
public static void handleAfterDeleteTest(){
List<Account> accountList = [SELECT Id, Name, Phone FROM Account
WHERE Name = 'Salesforce.com From Test Class'];
Test.startTest();
delete accountList;
Test.stopTest();
}

@IsTest
public static void handleAfterInsertTest(){
List<Account> accountList = [SELECT Id, Name, Phone, OwnerId
FROM Account WHERE Name = 'Salesforce.com From Test Class'];
Test.startTest();
AccountTriggerHandler.handleAfterInsert(accountList);
Test.stopTest();
}

ls
}

oo
Day 53 - SeeAllData & Test Utility Class in Salesforce

ch
Recording Link

https://youtu.be/bClSsd11XGQ
rS
Important Annotation

● @isTest(SeeAllData=false) - RECOMMENDED - If we use this annotation on


top of the Apex Class or Test Class method then you have to create the
he

object that is being used in the complete process.


● @isTest(SeeAllData=true) - NOT RECOMMENDED - If we use this annotation
on top of the Apex Class or Test class then the test class will read the data
nt

from Salesforce itself. This will start creating the problem when you are
trying to deploy the code to prod.
Pa

● Check SOQL Query in Main Class


○ Filters
○ Filter Values
■ Prepare Data Based on the Query Filter
● Check if Conditional Statements
○ Prepare Data Based on the Conditional Statements
Code used in the Class

@IsTest
public class ContactTriggerTest {

@TestSetup
public static void setupData(){

Account acc = TestUtility.prepareAccount('Salesforce.com From


Test Class', 'Education', 'Hot');
// Modify the Account Object

ls
acc.Description = 'From Test Class';
acc.AccountNumber = '67867873UHHJR';
insert acc;

oo
Contact con = TestUtility.prepareContact('Singh', 'Amit',
'[email protected]', acc.Id);
insert con;
ch
/*List<Account> accList = new list<Account>();
for(Integer i=0; i<200; i++){
Account acc1 = TestUtility.prepareAccount('Salesforce.com
rS
From Test Class '+i, 'Education', 'Hot');
acc1.Description = 'From Test Class '+i;
acc1.AccountNumber = '67867873UHHJR '+i;
accList.add(acc1);
he

}*/

}
nt

@IsTest(seeAllData = false)
public static void preventDuplicateContactBasedOnEmailBlankTest(){
List<Account> accList = [SELECT Id FROM Account WHERE Name =
'Salesforce.com From Test Class' LIMIT 1];
Pa

if(accList?.size()>0){
Contact con = TestUtility.prepareContact('Doe', 'John',
null, accList.get(0).Id);
try{
Test.startTest();
insert con;
Test.stopTest();
}catch(System.Exception e){
System.debug(e);
System.debug(e.getMessage());
}
}
}

@IsTest(seeAllData = false)
public static void preventDuplicateContactBasedOnEmailTest(){
List<Account> accList = [SELECT Id FROM Account WHERE Name =
'Salesforce.com From Test Class' LIMIT 1];
if(accList?.size()>0){
Contact con = TestUtility.prepareContact('Singh', 'Amit',
'[email protected]', accList.get(0).Id);
try{
Test.startTest();

ls
insert con;
Test.stopTest();
}catch(System.Exception e){

oo
System.debug(e);
System.debug(e.getMessage());
}
}
} ch
@IsTest
public static void preventDuplicateErrorTest(){
rS

}
}
he

@IsTest
public class TestUtility {

public static Account prepareAccount(String name, String industry,


nt

String rating){
Account acc = new Account(
Name = name,
Pa

Industry = industry,
Rating__c = rating,
Rating = rating
);
return acc;
}

public static Contact prepareContact(String lastName, String


firstName, String email, String accountId){
Contact con = new Contact(
FirstName = firstName,
LastName = lastName,
Email = email,
AccountId = accountId
);
return con;
}

Resources

ls
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/

oo
apex_qs_test.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_class_System_Assert.htm
● ch https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_test.htm
rS

Day 54 - Assert Class & runAs in Salesforce Test Classes


he

Recording Link
https://youtu.be/V4EhU58Kkyk
nt

Important Test Class Concept


Pa

● System.runAs(User) - To run a certain piece of code in the user context


○ Positive test Cases
○ Negative Test Cases
○ Business Logic
● @testVisible - This annotation will be used in the class on top of the private
variable or method to make that variable or method visible to Salesforce.
● Assert Class Methods
○ https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexr
ef/apex_class_System_Assert.htm
○ areEqual
○ areNotEqual
○ isTrue
○ isFalse

Test Class best Practices - Must Have

● Setup method
○ Utility

ls
● Test.startTest() & Test.stopTest()
● Assert Methods

oo
● Bulk Record Testing
● Use try & catch where applicable
● Positive & Negative Scenario
ch
Test Class Good to have

● runAs
rS
● Utility Class

Apex Code used in the Session


he

UserUtility Class

@IsTest
nt

public class UserUtility {

public static User prepareUser(String roleId, String profId, String


Pa

firstName, String lastName) {

String orgId = UserInfo.getOrganizationId();


String dateString = String.valueof(Datetime.now()).replace('
','').replace(':','').replace('-','');
Integer randomInt =
Integer.valueOf(math.rint(math.random()*1000000));
String uniqueName = orgId + dateString + randomInt;

User tempUser = new User(


FirstName = firstName,
LastName = lastName,
email = uniqueName + '@sfdc' + orgId + '.org',
Username = uniqueName + '@sfdc' + orgId + '.org',
EmailEncodingKey = 'ISO-8859-1',
Alias = uniqueName.substring(18, 23),
TimeZoneSidKey = 'America/Los_Angeles',
LocaleSidKey = 'en_US',
LanguageLocaleKey = 'en_US',
ProfileId = profId
);

if( String.isBlank(roleId) == false ){

ls
tempUser.UserRoleId = roleId;
}

oo
return tempUser;
}
}

ch
ContactTrigger Test

@IsTest
rS
public class ContactTriggerTest {

@TestSetup
public static void setupData(){
he

Account acc = TestUtility.prepareAccount('Salesforce.com From


Test Class', 'Education', 'Hot');
// Modify the Account Object
acc.Description = 'From Test Class';
nt

acc.AccountNumber = '67867873UHHJR';
insert acc;
Pa

Contact con = TestUtility.prepareContact('Singh', 'Amit',


'[email protected]', acc.Id);
insert con;

Profile p = [SELECT Id FROM Profile WHERE Name = 'PantherSchools


- Profile' LIMIT 1];
User tempUser = UserUtility.prepareUser('', p.Id, 'Amit',
'Singh');
//insert tempUser;
System.debug('tempUser '+tempUser.Id);
System.debug('Current User '+UserInfo.getUserId());
System.debug('Current User '+UserInfo.getFirstName());
System.debug('Current User '+UserInfo.getLastName());

System.runAs(tempUser){ // Only for Records


ErrorLog__c log = new ErrorLog__c(
Severity__c ='ERROR',
OwnerId = UserInfo.getUserId()
);
insert log; // this should not fail
// insert as user log;
// insert as system log;

ls
ErrorLog__c log1 = [SELECT Id, Name, Severity__c,
Owner.Name, OwnerId FROM ErrorLog__c WITH SYSTEM_MODE LIMIT 1];
// WITH USER_MODE

oo
// WITH SYSTEM_MODE
// WITH SECURITY_ENFORCED
System.debug(log1);
}
} ch
@IsTest(seeAllData = false)
public static void preventDuplicateContactBasedOnEmailBlankTest(){
rS
List<Account> accList = [SELECT Id FROM Account WHERE Name =
'Salesforce.com From Test Class' LIMIT 1];
if(accList?.size()>0){
Contact con = TestUtility.prepareContact('Doe', 'John',
he

null, accList.get(0).Id);
try{
Test.startTest();
insert con;
nt

Test.stopTest();
}catch(System.Exception e){
System.debug(e);
System.debug(e.getMessage());
Pa

}
}
}

@IsTest(seeAllData = false)
public static void preventDuplicateContactBasedOnEmailTest(){
List<Account> accList = [SELECT Id FROM Account WHERE Name =
'Salesforce.com From Test Class' LIMIT 1];
if(accList?.size()>0){
Contact con = TestUtility.prepareContact('Singh', 'Amit',
'[email protected]', accList.get(0).Id);
try{
Test.startTest();
insert con;
Test.stopTest();
}catch(System.Exception e){
System.debug(e);
System.debug(e.getMessage());
Assert.AreEqual('Insert failed. First exception on row
0; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, Duplicate contact
found! [email protected]: []',
e.getMessage(), 'No duplicate contact
found');

ls
Assert.isNotNull(e.getMessage(), 'No Exception found!');
}
}

oo
}

@IsTest
public static void preventDuplicateErrorTest(){

}
ch
@IsTest
rS
public static void checkDuplicateTest(){
Test.startTest();
//ContactTriggerHelper.checkDuplicate(null, null);
Test.stopTest();
he

}
nt

Resources
Pa

● How to Create User in Test Class


● System Class in Salesforce
● Assert Class in Salesforce
Day 55 - Introduction to Async & Future Apex in
Salesforce

Recording Link
https://youtu.be/JRUIfHgDwyI

Introduction Async [Asynchronous] Apex


→ Asynchronous Apex is used to run processes/operations whenever the resources

ls
are available to execute or we can say that with the help of Asynchronous Apex,
we can run the login in the near future.

oo
→ In this Asynchronous Apex the tasks are being executed in the background.

ch
→ Async Apex will be executed when the resources are free

There are various types of Asynchronous Apex


rS

● Future Apex
○ Method
● Queueable Apex
he

● Batch Apex
● Scheduled Apex
nt
Pa
ls
oo
ch
rS

Introduction to the future [Method] apex


→ Future methods run Asynchronously in the background whenever the resources
he

are free
→We should keep the logic in future methods when
nt

● Making Callout from Trigger


● Long Running Process needs to run in its own time
● Resolve the Mixed DML Operation
Pa

● You Need a higher set of governor limits like SOQL, DML, HeapSize, CPU
Time out Limit &, etc
○ 150 - DML
○ 200 - SOQL
○ 12MB - HeapSize
○ 60 Seconds CPU Timeout

Mixed DML Error


DML in Setup Object & Non-Setup Object in one go
● Setup Object
[https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode
/apex_dml_non_mix_sobjects.htm]
○ Insert User with Role
○ Permission Set Assignment
○ GroupMember
● Non Setup Object
○ Any Standard OR Custom Object that is not part of the Setup object

Contact is Created → Trigger Executed → Handler Class (Contact Manager)

ls
Contact is Created → Trigger will get Executed → Handler class method (contact

oo
manager) will be called → Put into Flex Queue → Transaction is broken

Structure of Future Method


ch
public static void mainMethod(){
testFutureMethod(); // 10 Second CPU Timeout
rS
method2();
// update account; // Some fields
}
he

@future
nt

public static void testFutureMethod(Account acc) { // Not allowed


// code_logic_here
Pa

update ACC; //possibility of data loss


}

@future
public static void testFutureMethod() { //allowed
// code_logic_here
}
→ Method must be public or global, and static in nature & method can not return
anything
→ Method must be annotated with @future
→ If the method is making a callout then the method must have @future(callout =
true)
→ Future methods only accept the primitive data types as parameters/arguments.
Id, String, Number, Decimal, etc.

Limitations of Future Method - Interview question

ls
1. Can not pass sObject or List<sObject> as a method parameter
2. Can not call future from future OR We can not chain future method

oo
3. We can not monitor future method because it does not return any Job Id
4. Future methods best work for the small size of records. 10 OR 20 Records
5. There can be only 50 future methods in a Single Transaction
ch
Code Used in the Session
rS

public class ContactManager {


he

@future
public static void createUser(Set<Id> recordIdsSet){
//dummyMethod();
List<Contact> newRecords = [SELECT Id, FirstName, LastName,
nt

Email FROM Contact WHERE Id IN: recordIdsSet];


List<User> userList = new List<User>();
Profile p = [SELECT Id FROM Profile WHERE Name = 'Standard
Platform User - Cloned' LIMIT 1];
Pa

UserRole roleRecord = [SELECT Id, Name FROM UserRole WHERE Name


= 'Western Sales Team' LIMIT 1];
for(Contact con: newRecords){
// Prepare the User
String orgId = UserInfo.getOrganizationId();
String dateString = String.valueof(Datetime.now()).replace('
','').replace(':','').replace('-','');
Integer randomInt =
Integer.valueOf(math.rint(math.random()*1000000));
String uniqueName = orgId + dateString + randomInt;

User tempUser = new User(


FirstName = con.FirstName,
LastName = con.LastName,
email = con.Email,
Username = uniqueName + '@sfdc' + orgId + '.org',
EmailEncodingKey = 'ISO-8859-1',
Alias = uniqueName.substring(18, 23),
TimeZoneSidKey = 'America/Los_Angeles',
LocaleSidKey = 'en_US',
LanguageLocaleKey = 'en_US',
ProfileId = p.Id
);

ls
tempUser.UserRoleId = roleRecord.Id; // MIXED_DML_OPERATION
ERROR WILL COME
userList.add(tempUser);

oo
}
insert userList;
}

ch
public static void test1(){
for(Integer i=0; i<49; i++)
dummyMethod();
System.debug('Outside of the for loop');
rS
if(true)
System.debug('Inside If');
System.debug('Outside of if statement');
he

}
}
nt

@future
public static void dummyMethod(){
Pa

}
}

Resources

● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_invoking_future_methods.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_dml_non_mix_sobjects_test_methods.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_invoking_future_methods.htm
● https://www.apexhours.com/future-method-in-salesforce/

ls
oo
ch
rS
he
nt
Pa
Day 56 - How Queueable Apex works in Salesforce -
Part 1

Recording Link
https://youtu.be/rVB8Vq8VJOY

Limitations of Future Apex

1. Can not future from future

ls
2. Can not track/monitor
3. Can not accept non-primitive data types

oo
Introduction Queueable Apex

● ch
Queueable apex is also part of Async apex and we can create any apex class
as a queueable class by implementing the System.Queueable interface
○ must need to implement execute(QueueableContext context) - Heart
rS
of Queueable Method
● The Queueable interface lets us add the jobs in the queue and lets them
monitor.
● System.enqueueJob method is used to add a job in Queue and returns an Id
he

to Monitor the Job.


● Queueable Job can accept both primitive and non-primitive data types as
input.
nt

● We can chain multiple Queueable Jobs by invoking another Queueable Job


from the running Queueable Job and this is helpful when the outcome for 1
Pa

job is the input of another job.


● We can call future methods from Salesforce Queueable Apex

Syntax

public class AsyncExecutionExample implements Queueable {


public void execute(QueueableContext context) {
Account a = new Account(Name='Acme',Phone='(415) 555-1212');
insert a;
}
}
ID jobID = System.enqueueJob(new AsyncExecutionExample());

ID jobID = System.enqueueJob( new AccountQueueable() ); //


//Flex Queue less than 100 [ Max 100 Record]
// Apex Class
AsyncApexJob jobInfo = [SELECT Status,NumberOfErrors
FROM AsyncApexJob WHERE Id=:jobID
];

ls
// Query Editor Code

oo
// SELECT Status, NumberOfErrors FROM AsyncApexJob WHERE Id =
'7072w00009JdOV4'

ch
Code used in the Class
rS

public class AsyncContactManager implements System.Queueable {

// Set<Id>
he

private List<Contact> newRecords; // Non-Static Variable -- Instance


Variable
private static String name;
public AsyncContactManager(List<Contact> contactList, String
nt

fullName){
this.newRecords = contactList;
if(String.isBlank(fullName)){
Pa

name = 'Amit Singh';


}else{
name = fullName;
}

public void execute(System.QueueableContext qc){


// Business Logic
Id jobId = qc?.getJobId();
List<AsyncApexJob> jobDetails = [SELECT Id, CreatedDate,
CreatedById, JobType, ApexClassId, ApexClass.Name,
Status, JobItemsProcessed,
TotalJobItems, NumberOfErrors, CompletedDate,
MethodName, ExtendedStatus,
ParentJobId, LastProcessed, LastProcessedOffset,
CronTriggerId
FROM AsyncApexJob
WHERE Id =:jobId
];
System.debug( JSON.serializePretty(jobDetails) );
System.debug(name);
System.debug( JSON.serializePretty(this.newRecords) );

ls
if(jobDetails?.size()>0){
// log the details into your custom object
return;

oo
}
processLogic();
// Id jobId = System.enqueueJob(manager, 2);
// Id jobId = System.enqueueJob(manager, 2);
}
/*
ch
* AsyncContactManager manager = new AsyncContactManager();
* Id jobId = System.enqueueJob(manager); // Flex Queue
rS
*
*/

/*
he

* AsyncContactManager manager = new AsyncContactManager();


* manager.execute(null);
*
*/
nt

public void processLogic(){


System.debug('Logic is Processing!');
}
Pa

/*
* AsyncContactManager manager = new AsyncContactManager();
* manager.processLogic();
*
*/
}

List<Contact> contactList = [SELECT Id, Name FROM Contact LIMIT 10];


AsyncContactManager manager = new AsyncContactManager(contactList, '');
Id jobId = System.enqueueJob(manager, 2); // Available
System.debug(jobId);
//AsyncContactManager manager1 = new AsyncContactManager(contactList,
'PantherSchools');

//AsyncContactManager manager = new AsyncContactManager();


//manager.execute(null);

SELECT Id, CreatedDate, CreatedById, JobType, ApexClassId,


ApexClass.Name, Status, JobItemsProcessed, TotalJobItems,

ls
NumberOfErrors, CompletedDate, MethodName, ExtendedStatus, ParentJobId,
LastProcessed, LastProcessedOffset, CronTriggerId FROM AsyncApexJob
WHERE Id = '707dL000006PHDI'

oo
Exercise - Queueable Apex

ch
Create a queueable job apex job to process all the accounts that do not have
phone numbers and create a task under those accounts and assign it to the
Account Owner.
rS

Below is the information about the Task record

● Subject - Phone Number is missing


he

● Status - Not Started


● Priority - Urgent
● Description - The phone number for the account + “account name” is
nt

missing. Please collect the information asap


● Owner - Account Owner
● Related To - Account
Pa

Resources

● System.enqueueJob
● Queueable Content Interface
● Queueable Interface
● Async Apex Job
Day 57 - How Queueable Apex works in Salesforce -
Part 2

Recording Link
https://youtu.be/7Tef8SwgdEU

Queueable Apex Limits

● You can add up to 50 jobs to the queue with System.enqueueJob in a single

ls
transaction.
● There is no depth limit for chaining the job which means we can chain as

oo
many jobs as we want.
○ Id jobId = System.enqueueJob(manager, 2);
■ Id jobId = System.enqueueJob(manager, 2)
ch ● Id jobId = System.enqueueJob(manager, 2)
○ Id jobId = System.enqueueJob(manager, 2)
■ Id jobId = System.enqueueJob(manager, 2)
rS
● For the developer edition, the max depth for the chaining job is 5.
○ FATAL_ERROR System.AsyncException: Maximum stack depth has
been reached.
he

● When calling/chaining jobs with System.enqueueJob method, you can add


only one job from a parent job.

Queueable vs Future
nt

Future Queueable
Pa

Future is a Method Queueable is Apex Class

Can not accept the non-primitive data Can accept both primitive and Non
types Primitive data types

Can not call the future method from the The Future method can be called
future method from Queueable apex and vice versa
We can not track the future method We can easily monitor the jobs

Method chaining is not possible Method/Job chaining is possible

Possibility of Data loss in case of passing Possibility of Data loss in case of


the non-primitive data types passing the non-primitive data types

ls
Practice Scenario

oo
Mass Data Update with Error Handling:

● ch
Scenario: Update the Customer_Status__c field on all Account records
based on their last activity date. If an account has had no activity in the last
year, set the status to 'Inactive'. For any record that fails to update, log the
rS
error details into a custom object called Failed_Update_Log__c.
● Purpose: Practice handling large datasets and managing exceptions in
asynchronous processing. This scenario highlights the importance of error
logging and ensures that no record is left unprocessed due to errors.
he

Chaining Queueable Jobs:

● Scenario: Implement a process where, upon completion of updating


nt

Opportunities' Stage__c field, the related Contacts' Last_Activity__c field is


updated. Once Contacts are updated, update the Cases' Priority__c field.
Pa

Each step should only start after the previous one has completed
successfully.
● Purpose: Learn to chain Queueable jobs to manage dependent processes,
ensuring each step completes before the next begins. This helps in
managing complex workflows that cannot be handled in a single transaction
due to governor limits.

Data Cleanup Operation:


● Scenario: Identify and delete all Leads that have not been contacted in the
last two years and have a status of 'Cold'. Ensure the process logs the
details of each deleted Lead into a custom object for audit purposes.
● Purpose: Understand the best practices for safely deleting large volumes of
records. Emphasize the importance of logging and ensuring no critical data
is lost inadvertently.

External System Integration:

● Scenario: Make an API call to an external service to fetch the latest

ls
currency exchange rates and update a custom object Exchange_Rate__c in
Salesforce. Handle any errors from the API call and ensure retry logic for

oo
transient failures.
● Purpose: Get experience with callouts in Queueable Apex and handle the
asynchronous nature of such operations. Learn how to manage API limits
ch
and handle retries.

Recalculate Roll-Up Summary Fields:


rS
● Scenario: Recalculate a custom roll-up summary field Total_Sales__c on the
Account object based on the related Opportunities' Amount field. Ensure
the recalculations are done asynchronously to avoid hitting governor limits.
he

● Purpose: Practice recalculating aggregate data and managing bulk updates


to ensure efficient processing. Understand the importance of handling large
volumes of data in a scalable manner.
nt

Email Notification After Processing:

● Scenario: Process a batch of records, such as updating the Status__c field


Pa

on all Cases. Upon completion, send an email to the case manager with a
summary of how many cases were updated and details of any errors
encountered.
● Purpose: Combine data processing with communication to end users. Learn
how to generate and send summary emails after an asynchronous process
completes.

Data Migration:
● Scenario: Migrate data from a custom object Old_Products__c to a standard
object Product2. Ensure that all necessary transformations (e.g., mapping
old field names to new ones) are handled correctly. Log any issues during
the migration process.
● Purpose: Practice data transformation and migration techniques, ensuring
data integrity and consistency during large-scale migrations.

Complex Data Validation:

● Scenario: Validate all email addresses in the Contact object to ensure they

ls
follow a proper format (e.g., [email protected]). Log any records with
invalid emails into a custom object Invalid_Emails_Log__c.

oo
● Purpose: Understand how to perform bulk data validation and handle
exceptions. Ensure data quality by identifying and logging invalid records for
further action.
ch
Generate and Store Large Reports:

● Scenario: Generate a report that aggregates sales data from multiple


rS
objects (e.g., Accounts, Opportunities, and Orders). Store the report in a
custom object Sales_Report__c or as an attachment.
● Purpose: Learn how to handle large datasets and generate comprehensive
he

reports. Understand the storage options and best practices for handling
large volumes of report data.

Processing Hierarchical Data:


nt

● Scenario: Recalculate the Total_Revenue__c field on all parent Accounts


based on their child Opportunities' Amount field. Ensure the recalculations
Pa

correctly propagate up the hierarchy and handle any dependencies.


● Purpose: Practice handling hierarchical data relationships and dependencies
in bulk processing. Ensure accurate calculations and updates in a
hierarchical data structure.

public class AsyncContactManager implements System.Queueable {

private List<Contact> newRecords;


private static String name;
public AsyncContactManager(List<Contact> contactList, String
fullName){
this.newRecords = contactList;
if(String.isBlank(fullName)){
name = 'Amit Singh';
}else{
name = fullName;
}

public void execute(System.QueueableContext qc){

ls
// Business Logic
Id jobId = qc?.getJobId();
List<AsyncApexJob> jobDetails = [SELECT Id, CreatedDate,

oo
CreatedById, JobType, ApexClassId, ApexClass.Name,
Status, JobItemsProcessed,
TotalJobItems, NumberOfErrors, CompletedDate,
MethodName, ExtendedStatus,
ch
ParentJobId, LastProcessed, LastProcessedOffset,
CronTriggerId
FROM AsyncApexJob
WHERE Id =:jobId
rS
];
System.debug( JSON.serializePretty(jobDetails) );
System.debug(name);
System.debug( JSON.serializePretty(this.newRecords) );
he

if(jobDetails?.size()>0){
//return;
}
// Can we call the future from the future? - No
nt

// Can we call the future from Queueable? - Yes [We can track
queueable, As per salesforce document we can call 1 future from
queueable]
// Can we call queueable from future - Yes
Pa

// How many queueable job can queued/called from future - 1 [If


we try to queue more than 1 job - FATAL_ERROR System.LimitException: Too
many queueable jobs added to the queue: 2]

//ContactManager.createUser(null);
//ContactManager.dummyMethod();

processLogic();
}

public void processLogic(){


System.debug('Logic is Processing!');
// outcome of first job --> input of second job --> output of
second job is the input of third job --------
Id jobId1 = System.enqueueJob(new AsyncAccountManager());
System.debug(jobId1);

public class AsyncAccountManager implements System.Queueable {

ls
public void execute(System.QueueableContext context){
System.debug(' AsyncAccountManager ');

oo
Id jobId2 = System.enqueueJob(new AsyncLeadManager());
System.debug(jobId2);
}

}
ch
public class AsyncOpportunityManager implements System.Queueable {
rS

public void execute(System.QueueableContext context){


System.debug(' AsyncOpportunityManager ');
he

Id jobId2 = System.enqueueJob(new AsyncCaseManager());


System.debug(jobId2);
}
nt

public class AsyncCaseManager implements System.Queueable {


Pa

public void execute(System.QueueableContext context){


System.debug(' AsyncCaseManager ');
Id jobId2 = System.enqueueJob(new AsyncTaskManager());
System.debug(jobId2);
}

public class AsyncTaskManager implements System.Queueable {


public void execute(System.QueueableContext context){
System.debug(' AsyncTaskManager ');

Resources

● System.enqueueJob
● Queueable Content Interface

ls
● Queueable Interface
● Async Apex Job

oo
DAY 57.01 - Introduction to Batch Apex in Salesforce
ch
Recording Link
https://youtu.be/7Tef8SwgdEU
rS

● A developer can use a batch apex when needed to perform complex and
he

long-running processes using Salesforce Apex that can process thousands


and millions of records. || 50 Million
● To utilise the power of batch apex developers need to implement a
nt

Database.Batchable<sObject> interface
● Batch apex has 3 methods
○ Start - Only once and will always execute
Pa

■ Database.QueryLocator - Apex Class


■ Database.Iterator - Apex Class (Not Included)
● Only Process 50K Records
○ Execute - Heart of batch apex
■ Business Logic will be there inside execute method
○ Finish - Only once and will always execute
■ Perform Cleanup Logic
● Sending the Email
● Calling another batch apex ( same )
● A batch apex can process up to 50 million records at a time
● The default batch size of the batch apex is 200 records
● Min Batch Size - 1 Record
● Max Batch Size
○ If the start method is QueryLocator then 2000 Records
○ If the start method is Iterator then no upper limit

public class AccountsBatchClass implements Database.Batchable<sObject>{

ls
public Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator('SELECT Id, Name FROM Account Where

oo
Phone = null');
}

ch
public void execute(Database.BatchableContext BC, List<Account> scope){
}
rS
public void finish(Database.BatchableContext BC){
}

}
he

Start Method
nt

→ Start method of batch apex only executes once and it will always be executed
Pa

even if the Query is returning 0 row


→ The return type of the start method is Database.QueryLocator class or Iterable
class.
→ Start method does not return any records it does return the locator only or
iterator

public Database.QueryLocator start(Database.BatchableContext BC){

return Database.getQueryLocator('SELECT Id, Name FROM Account Where


Phone = null');

→ Database.BatchableContext returns the jobId or child job Id for the batch apex
for monitoring purposes.

Execute Method

ls
→ Execute method of batch only executes when there are records returned from
the start method query locator.

oo
→ Execute method execute depends upon the size of records and batch size
→ No of records / Chink Size
→ 5000 / 200 ~= 25
ch
→ 110 / 200 ~= 1
→ 250 / 200 ~= 2
rS

public void execute(Database.BatchableContext BC, List<sObject> scope){


// Separate Transaction
// Job Id - different
he

// BC.getJobId() - Monitor Batch Job


// bc.getChildJobId() - Execute Method Id - Monitor the chunk Execute Method
// 10th - Error in DML
nt

}
Pa

Finish Method

→ Finish method always executes and executes only once


→ Finish method can be used to have cleanup logic like sending emails or calling
another batch apex from the apex batch

public void finish(Database.BatchableContext BC){


// Send Email
// Call another batch Job
}

Code Used in the Session

public class AccountBatchClass implements Database.Batchable<sObject>{

public Database.QueryLocator start(Database.BatchableContext

ls
batchContext){
String query = 'SELECT Id, Name, Email, Title, Source__c FROM
Lead LIMIT 10';

oo
return Database.getQueryLocator([SELECT Id, Name, Email, Title
FROM Lead LIMIT 10]);
}

ch
public String message(){
return '123';
}
rS
public void execute(Database.BatchableContext batchContext,
List<Lead> leadRecords){

}
he

public void finish(Database.BatchableContext batchContext){

}
nt

}
Pa

Resources
● Batchable Interface
● QueryLocator Class
● Database Class
DAY 58 - Batch Apex in Salesforce Continue &
Stateful Interface

Recording Link
https://youtu.be/gUDRRdfMxrs

Batch Job Status

● Holding - The job has been submitted to the flex queue and waiting for

ls
resources to become available
● Queued - The job is waiting for execution

oo
● Preparing - The start method of the batch apex is invoked
● Processing - The executed method is being executed
● Aborted - Aborted by the user of From Apex Class


ch
Completed - Job completed with or without failures.
Failed - The job experienced a system failure.
rS
Id jobId = Database.executeBatch(instance of batch apex);
Id jobId = Database.executeBatch(instance of batch apex, batch size);

Execute Method
he

→ Execute method of batch only executes when there are records returned from
the start method query locator.
→ Execute method execute depends upon the size of records and batch size
nt

→ No of records / Chink/Btach Size


→ 5000 / 200 ~= 25
→ 110 / 200 ~= 1
Pa

→ 250 / 200 ~= 2
1st execute method has failed
2nd execute method has passed - [50 records]

Important Questions

Question - Can we call a batch from batch Apex?


Answer - Yes. From Finish Method
Question - Can we call a queueable apex from the batch apex?

Question - Can we call a batch from the Queueable?

Question - Can we call the future method from the batch apex?

Question - Can we call the batch apex from the future method?

ls
Question - What is the maximum number of async methods that can be executed
in total in 24 hours? - 2,50,000 OR total no of Salesforce Licence * 200 whichever

oo
is greater in 24 hours

Question - Can we make the callout from the batch apex?


ch
Answer - Yes, for this we need to implement an interface Database.AllowsCallouts
Error - System.LimitException: Too many callouts: 1
rS
Question - Can we make the callout from the future method?
Answer - @future(callouts = true)
Error - Callouts, are not allowed from the future method
he

Question - Can we make the callout from the queueable apex?


Answer - Yes, for this we need to implement an interface Database.AllowsCallouts
-
nt

Error - System.LimitException: Too many callouts: 1

Code Used in the Session


Pa

public class AccountBatchClass implements Database.Batchable<sObject>{

public Database.QueryLocator start(Database.BatchableContext


batchContext){
System.debug('Start Executed');
return Database.getQueryLocator([SELECT Id, Name, Email, Title,
Description FROM Lead]);
}

public void execute(Database.BatchableContext batchContext,


List<Lead> leadRecords){
System.debug('Lead Records '+ leadRecords.size());
for(Lead l : leadRecords){
l.Description = l.Email;
}
update leadRecords;
}

public void finish(Database.BatchableContext batchContext){


System.debug('Finish Executed');
Database.executeBatch(new ContactBatchClass());

ls
}

oo
public class ContactBatchClass implements Database.Batchable<sObject>{

ch
public Database.QueryLocator start(Database.BatchableContext
batchContext){
System.debug('Start Executed');
//Id jobId = System.enqueueJob(new AsyncContactManager(null,
rS
''));
return Database.getQueryLocator([SELECT Id, Name, Email, Title,
Description FROM Contact]);
}
he

public void execute(Database.BatchableContext batchContext,


List<Contact> contactRecords){
for(Contact con: contactRecords){
nt

con.Description = con.Id;
}
Database.update(contactRecords, false);
Pa

//Id jobId = System.enqueueJob(new AsyncContactManager(null,


''));

public void finish(Database.BatchableContext batchContext){


System.debug('Finish Executed');
Id jobId = System.enqueueJob(new AsyncContactManager(null, ''));
}

}
Assignments

Bulk Territory Assignment:

● Scenario: Assign Sales Territories to all Account records based on


their geographic location. Use a custom field Geolocation__c (Text
Field) to determine the appropriate territory and update the

ls
Territory__c(Lookup Field) field on each Account.
● Prerequisite: Create a custom Object Territory with the API Name

oo
(Territory__c) and this object will have 2 fields. Name a standard Field
and Active checkbox field.
○ Note:- You can expand this object to create Address fields to
chstore the address details
● Purpose: Learn how to handle large-scale record updates based on
geographic criteria and ensure efficient territory management.
rS

Bulk Calculation of Custom Metrics:


he

● Scenario: Calculate and update a custom metric, such as


Customer_Lifetime_Value__c on Contact records based on their
related Opportunity history. Sum up the total Amount field of all
nt

closed-won Opportunities for each Contact.


● Purpose: Practice performing bulk calculations and updates based on
related records, enhancing data analytics capabilities.
Pa

Mass Price Book Update:

● Scenario: Add all the Products under the Custom newly created
'Seasonal Sale' Price Book. To attach the Products under the
Pricebook you need to Create the record of Price Book Entry Object.
○ Note:- Get the PriceBook Entry that is associated with the
Standard Price Book
○ Apply 10% discount on the UnitPrice Found for the standard
price book and then Assign the discounted amount as UnitPrice
● Prerequisite: Create a New PriceBook record with the Name
“Seasonal Sale”
● Purpose: Gain experience with updating large datasets and managing
product pricing efficiently.

Historical Data Auditing:

ls
● Scenario: Audit historical Case records to ensure all required fields
are populated (Subject, Description, Status, Priority, Origin, Contact &

oo
Product). If any required fields are missing, log the details in a custom
object Audit_Log__c for review.
○ Please create the required fields on the Custom Object. Some
ch Important fields are there
○ Case - Lookup
○ Opportunity - Lookup
rS

○ Lead - Lookup
● Purpose: Understand how to perform data audits and ensure data
completeness and compliance with organizational standards.
he

Automated Customer Segmentation:

● Scenario: Segment all Contacts into different customer categories


nt

(e.g., VIP, Regular, New) based on their purchase history and


engagement levels. Update a custom field Customer_Segment__c on
Pa

each Contact.
○ Note:- Purchase History will be taken from the related Closed
Won Opportunities.
○ Here is how the Levels will be decided
■ Amount < 10000 → New
■ Amount > 10000 & Amount < 100000 → Redular
■ Amount > 100001 → VIP
● Purpose: Learn how to categorize and segment large datasets based
on custom business logic and criteria.
Bulk Case Closure:

● Scenario: Automatically close Cases that have been in a 'Pending'


status for more than 90 days. Update the Status field to 'Closed' and
add a note indicating the automated closure.
● Purpose: Practice automating record status updates and managing
large volumes of cases efficiently.

Mass Email Opt-Out Management:

ls
● Scenario: Process a list of Contacts who have opted out of email
communications. Update the Email_Opt_Out__c custom checkbox

oo
field OR You can use Standard Field present on the Contact to true
for all Contacts in the provided list.
● Purpose: Handle large-scale opt-out requests and ensure compliance
ch
with email communication preferences.

Database.Stateful Interface
rS

Maintains the states of the batch instance variables


he

public class ContactBatchClass implements Database.Batchable<sObject>,


Database.Stateful {
public Integer successRecords = 0;
nt

public Integer failedRecords = 0;


public Integer totalRecords = 0;
public Database.QueryLocator start(Database.BatchableContext
batchContext){
Pa

System.debug('Start Executed');
return Database.getQueryLocator([SELECT Id, Name, Email, Title,
Description FROM Contact]);
}

public void execute(Database.BatchableContext batchContext,


List<Contact> contactRecords){
totalRecords += contactRecords.size(); // totalRecords =
totalRecords + contactRecords.size();
System.debug(' totalRecords '+ totalRecords);
for(Contact con: contactRecords){
con.Description = con.Id;
}
List<Database.SaveResult> results =
Database.update(contactRecords, false);
for(Database.SaveResult sr: results){
if(sr.IsSuccess()){
successRecords += 1;
}else{
failedRecords += 1;
}
}
System.debug(' successRecords '+ successRecords);

ls
System.debug(' failedRecords '+ failedRecords);
}

oo
public void finish(Database.BatchableContext batchContext){
System.debug('Finish Executed');
System.debug(' totalRecords in finish Method '+ totalRecords);
System.debug(' successRecords in finish Method '+

}
ch
successRecords);
System.debug(' failedRecords in finish Method '+ failedRecords);
rS
}

Resources
● Batchable Interface
he

● QueryLocator Class
● Database Class
nt

Day 59 - Scheduled Apex in Salesforce


Pa

Recording Link
https://youtu.be/MC2lUhDjfuU

Introduction

If we wanted to execute the code at a certain time or at a certain interval then we


use Schedule Apex. For Example

1. Daily
2. Weekly
3. Every Hour
4. Every Month
5. Etc.

Schedulable Interface → System namespace


Execute → SchedulableContext

public class ContactsBatchSchedule implements System.Schedulable {

public void execute(SchedulableContext SC) {

ls
ContactsBatchClass batchClass = new ContactsBatchClass();
Database.executeBatch(batchClass);
}

oo
}
ch
Every 15 min
rS

CRON Expression
he

Here is the simple expression which will run at 60 minutes of an hour


60/5 → 12
60/10 → 6
60/15 → 4
nt

0 00 * * * ? // 60 minutes of an hour (every hours)


Pa

0 05 * * * ? // 5 minutes of an hour
// 12:00 PM → 12:05
// 10:00 AM → 10:05 → 11:05 → 12:05

0 10 * * * ? // 10 minutes of an hour
0 15 * * * ?
0 20 * * * ?
0 25 * * * ?
0 30 * * * ?
0 35 * * * ?
0 15 * * * ? // 15 minutes of an hour

0 30 * * * ? // 30 minutes of an hour

0 45 * * * ? // 45 minutes of an hour

Here is the explanation of the above cron expression

ls
● 00 Seconds

oo
● 00 Minute
● Every Hour
● Every Day
● ch
Every week

CRON Expression for Every 5 Minutes


rS

System.schedule('YourScheduler 1', '0 00 * * * ?', new YourScheduler());


System.schedule('YourScheduler 2', '0 05 * * * ?', new YourScheduler());
he

System.schedule('YourScheduler 3', '0 10 * * * ?', new YourScheduler());


System.schedule('YourScheduler 4', '0 15 * * * ?', new YourScheduler());
System.schedule('YourScheduler 5', '0 20 * * * ?', new YourScheduler());
nt

System.schedule('YourScheduler 6', '0 25 * * * ?', new YourScheduler());


System.schedule('YourScheduler 7', '0 30 * * * ?', new YourScheduler());
System.schedule('YourScheduler 8', '0 35 * * * ?', new YourScheduler());
Pa

System.schedule('YourScheduler 9', '0 40 * * * ?', new YourScheduler());


System.schedule('YourScheduler 10', '0 45 * * * ?', new YourScheduler());
System.schedule('YourScheduler 11', '0 50 * * * ?', new YourScheduler());
System.schedule('YourScheduler 12', '0 55 * * * ?', new YourScheduler());

CRON Expression to run Every 15 minutes


SchedulableClass obj = new SchedulableClass();

String sch1 = '0 0 * * * ?';


System.schedule('Every 0 Minute', sch1, obj);

String sch2 = '0 15 * * * ?';


System.schedule('Every 15 Minute', sch2, obj);

ls
String sch3 = '0 30 * * * ?';
System.schedule('Every 30 Minute', sch3, obj);

oo
String sch4 = '0 45 * * * ?';
System.schedule('Every 45 Minute', sch4, obj);
ch
CRON Expression to run Every 30 minutes
rS
SchedulableClass obj = new SchedulableClass();

String sch1 = '0 0 * * * ?';


System.schedule('Every 0 Minute', sch1, obj);
he

String sch2 = '0 30 * * * ?';


System.schedule('Every 30 Minute', sch2, obj);
nt

Code used in the Session


Pa

public class ContactBatchClassSchedulable implements System.Schedulable,


Database.AllowsCallouts {

public void execute(System.SchedulableContext sc){


// heart of Schedulable Apex
System.debug('Schedulable Apex ');
Id jobId = Database.executeBatch(new ContactBatchClass());
System.debug('ContactBatchClass JobId '+jobId);
// whole Logic
// Call the Normal Apex CLass method
// Make the Callout
validate();
}

public void validate(){


// business login here
}
}

public class AsyncContactManager implements System.Queueable {

private List<Contact> newRecords;

ls
private static String name;
public AsyncContactManager(List<Contact> contactList, String
fullName){

oo
this.newRecords = contactList;
if(String.isBlank(fullName)){
name = 'Amit Singh';

}
ch
}else{
name = fullName;

}
rS
public void execute(System.QueueableContext qc){
// Business Logic
Id jobId = qc?.getJobId();
List<AsyncApexJob> jobDetails = [SELECT Id, CreatedDate,
he

CreatedById, JobType, ApexClassId, ApexClass.Name,


Status, JobItemsProcessed,
TotalJobItems, NumberOfErrors, CompletedDate,
MethodName, ExtendedStatus,
nt

ParentJobId, LastProcessed, LastProcessedOffset,


CronTriggerId
FROM AsyncApexJob
Pa

WHERE Id =:jobId
];
System.debug( JSON.serializePretty(jobDetails) );
System.debug(name);
System.debug( JSON.serializePretty(this.newRecords) );
if(jobDetails?.size()>0){
//return;
}
processLogic();
}

public void processLogic(){


System.debug('Logic is Processing!');
// outcome of first job --> input of second job --> output of
second job is the input of third job --------
if(!Test.isRunningTest()){ // Check if the test is running
Id jobId1 = System.enqueueJob(new AsyncAccountManager());
System.debug(jobId1);
}
}
}

ls
Resources

● System Class

oo
● CRON Maker
● CronTab
● CRONTab Guru

ch
Day 59.1 - Write Unit Test Cases for the Async Apex

Recording Link
rS
https://youtu.be/MC2lUhDjfuU

Code Used in the Session


he

@IsTest
public class ContactBatchClassSchedulableTest {
nt

@TestSetup
public static void setUpData(){
List<Contact> contactList = new List<Contact>();
Pa

Account acc = TestUtility.prepareAccount('Salesforce.com',


'Education', 'Warm');
insert acc;

for(Integer i=0; i<80; i++){


Contact con = new Contact(
LastName = ' Singh '+i,
FirstName = 'Amit '+i,
Email = 'amit.singh'+i+'@gmail.com',
AccountId = acc.Id,
Title = 'Salesforce Developer'
);
contactList.add(con);
}
insert contactList;
}

@IsTest
public static void contactBatchTest(){
Test.startTest();
ContactBatchClassSchedulable scheduler = new
ContactBatchClassSchedulable();

ls
scheduler.execute(null);
Test.stopTest();
List<Contact> contactList = [SELECT Id, Name, Email, Phone,

oo
Description FROM Contact LIMIT 200];
for(Contact con: contactList){
Assert.areEqual(con.Id, con.Description, 'Batch is failed to
updated description!');

}
}
}ch
rS
@IsTest
public class AsyncContactManagerTest {

@TestSetup
he

public static void setupData(){


List<Contact> contactList = new List<Contact>();

Account acc = TestUtility.prepareAccount('Salesforce.com',


nt

'Education', 'Warm');
insert acc;
Pa

for(Integer i=0; i<200; i++){


Contact con = new Contact(
LastName = ' Singh '+i,
FirstName = 'Amit '+i,
Email = 'amit.singh'+i+'@gmail.com',
AccountId = acc.Id,
Title = 'Salesforce Developer'
);
contactList.add(con);
}
insert contactList;
}
@IsTest
public static void asyncContactManagerTest(){
AsyncContactManager manager = new AsyncContactManager(null, '');
Test.startTest();
Id jobId = System.enqueueJob(manager, 10);
Test.stopTest();
}

@IsTest
public static void asyncContactManagerTest1(){
AsyncContactManager manager = new AsyncContactManager(null,

ls
'Amit Singh');
Test.startTest();
Id jobId = System.enqueueJob(manager, 10);

oo
Test.stopTest();
}

@IsTest
ch
public static void asyncContactManagerTest2(){

List<Contact> contactList = [SELECT Id, Name, Email, Phone FROM


Contact LIMIT 200];
rS

AsyncContactManager manager = new


AsyncContactManager(contactList, 'Amit Singh');
Test.startTest();
he

Id jobId = System.enqueueJob(manager, 10);


Test.stopTest();

Assert.isNotNull(jobId);
nt

AsyncApexJob jobDetail = [SELECT Id, ApexClassId, Status FROM


AsyncApexJob WHERE Id =:jobId LIMIT 1 ];
Assert.areEqual('Completed', jobDetail.Status);
Pa

}
}
Day 60 - Exception Handling in Salesforce

Recording Link
https://youtu.be/JQveML8ntMQ

Exception in Salesforce

Exception in the Apex is the error that disturbs the normal execution of the flow.
In Salesforce we use to try, catch, and finally handle the error. The exception can

ls
happen at any place and some of the Examples are given below

oo
● DML Exception
● Query - Limit 50000
● CPU Time Out - 10 Seconds / 60 Seconds

ch
Null Pointer Exception
○ Microsoft Global Outage
■ Null-Pointer Exception
rS

○ Crowdstrike
● Security
● HeapSize - 6 MB / 12 MB
he

● Query Exception
● sObject Exception
● ListException
nt

● etc

There are various ways to handle the exception as well like using the boolean
Pa

variables, if condition, a return statement, Limits Class, and other code blocks.

Different Types of Exception

● DML Exception
● FinalException
● ListException - List index out of bounds
● NullPointerException -
● QueryException - Limit 50000 [ Too Many SOQL Query Rows ]
● sObjectException - SObject row was retrieved via SOQL without querying
the requested field:
● LimitException - Governor Limit [Limits Class] -
https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_limits.htm
○ Too Many SOQL 101
○ Too Many DML Statements 151
○ To0 Many DML Statements 2
○ Too Many DML Rows 10001

ls
○ Too Many Callouts 101
○ Too Many Callouts 2

oo
● etc

For more information, please read the given article


https://www.apexhours.com/different-types-of-exceptions-in-salesforce/
ch
Null-Pointer Exception
rS
String fullName; // Amit Singh
// safe navigation operator (?.) - started using it
// null coalescing operator ??
// conditional statement - widely used
he

// Amit Sinhg
//if(fullName==null){
// fullName = 'Amit Singh';
//}
nt

fullName = fullName ?? 'Barbara Levi';


Pa

List<String> names1 = fullName?.split(' ');


System.debug(names1);
if(fullName !=null){
List<String> names = fullName.split(' ');
System.debug(fullName);
}
print('Amit Singh');
public static void print(String fullName){
if(fullName !=null){
List<String> names = fullName.split(' ');
System.debug(names);
System.debug(fullName);
}
}

Account acc; // null


acc = acc ?? new Account();
acc.Name = 'Salesforce.com';

Important Points
To handle the exception in Salesforce we use try, catch & finally block

ls
● try, catch & finally are the block & final is the keyword
● try block will be used with either catch block or final block

oo
try{

ch
}catch(System.DmlException ex){

}finally{
rS

}
he

try{
nt

}finally{
Pa

try{

}catch(System.DmlException ex){

● we can have multiple catch blocks with the single try block
try{
insert accList;
}catch(System.DmlException ex){

}catch(System.Exception ex){

}finally{
//always
}

ls
● finally block will always get executed

oo
● Exception is the Parent Class of any exception
● Any Exception has the following common method
○ getCause
ch ○

getLineNumber
getMessage
○ getStackTraceString
rS
○ getTypeName
○ initCause
○ setMessage
he

Code used in the Session

DML Exception
nt

Contact con = new Contact();


con.FirstName = 'Ravi';
Pa

con.LastName = 'Sanders';
con.Email = '[email protected]';
con.AccountId = '001dL000001rKZeQAM';
String fullName;
try{
List<String> names = fullName.split(' ');
insert con; // DML Exception
}catch(System.DMLException ex){ // Handling the Exception
System.debug('DMLException');
System.debug(ex.getMessage());
System.debug(ex.getCause());
System.debug(ex.getStackTraceString());
}catch(System.NullPointerException ex){ // Handling the Exception
System.debug('NullPointerException');
System.debug(ex.getMessage());
System.debug(ex.getCause());
System.debug(ex.getStackTraceString());
}catch(System.Exception ex){
System.debug('Exception');
System.debug(ex.getMessage());
System.debug(ex.getCause());
System.debug(ex.getStackTraceString());
}finally{

ls
// Always get execute
// cleanup logic
// clear the collection variables

oo
}

// trigger --> handler --> helper --> DML --> trigger -> handler -->
ch
helper --> DML --> exception

List<String> fruits = new List<String>();


fruits.add('Banana');
rS
if(fruits.size() > 1){
String ab = fruits.get(1);
}
he

Integer index = 10;


if(fruits.size() > index){
String ab = fruits.get(index);
}
nt

Null-Pointer Exception
Pa

String fullName; // Amit Singh


// safe navigation operator (?.) - started using it
// null coalescing operator ??
// conditional statement - widely used

// Amit Sinhg
//if(fullName==null){
// fullName = 'Amit Singh';
//}

fullName = fullName ?? 'Barbara Levi';


List<String> names1 = fullName?.split(' ');
System.debug(names1);
if(fullName !=null){
List<String> names = fullName.split(' ');
System.debug(fullName);
}
print('Amit Singh');
public static void print(String fullName){
if(fullName !=null){
List<String> names = fullName.split(' ');

ls
System.debug(names);
System.debug(fullName);
}

oo
}

Account acc; // null


acc = acc ?? new Account();
ch
acc.Name = 'Salesforce.com';

Resources
rS

● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_classes_exception_methods.htm?q=DmlException
he

● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_classes_exception_methods.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
nt

_methods_system_string.htm#apex_System_String_split
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_exception_definition.htm
Pa
Day 61 - Exception Handling Framework for Apex in
Salesforce

Recording Link
https://youtu.be/mAyR0jLNEYc

Introduction

ls
An exception handling framework is important in software development for several
reasons. In the context of Apex and Salesforce development, it ensures that your
application remains robust, user-friendly, and maintainable. Here are the key

oo
reasons why an exception handling framework is essential:

● Improved Reliability
● Enhanced User Experience



ch
Easier Maintenance and Debugging
Separation of Concerns
Consistency
● Compliance and Auditing
rS
● Prevention of Data Corruption.
● Resource Management

Code Used in the session


he

public class Logger {

public static ErrorLog__c log(System.Exception ex, String className,


nt

String methodName, String userId, String severity, String typex, String


title){
ErrorLog__c log = new ErrorLog__c();
Pa

log.AffectedUser__c = userId;
log.ApexClassName__c = className;
log.ApexMethodName__c = methodName;
log.Severity__c = severity;
log.ComponentType__c = typex;

log.StackTraceString__c = ex?.getStackTraceString();
log.ErrorContent__c = 'Exception Message : '+ ex?.getMessage()
+' : Exception Cause '+ ex?.getCause()
+' : Line Number '+ex?.getLineNumber()+' : Exception
Type '+ex?.getTypeName();
log.ErrorTitle__c = title;

return log;
}

public static ErrorLog__c log(String errorContent, String className,


String methodName, String userId, String severity, String typex, String
title){
ErrorLog__c log = new ErrorLog__c();
log.AffectedUser__c = userId;
log.ApexClassName__c = className;

ls
log.ApexMethodName__c = methodName;
log.Severity__c = severity;
log.ComponentType__c = typex;

oo
log.ErrorContent__c = errorContent;

log.ErrorTitle__c = title;
return log;

}
} ch
rS
public class ContactTriggerHelper {

public static void


preventDuplicateContactBasedOnEmailAfterUndelete(List<Contact>
he

newRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set
nt

* 1. Get the Existing Records of Contact Object based on


Email
* 2. Check if the record found
Pa

* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
if(String.isBlank(con.Email) == false){ //
[email protected]
emailSet.add(con.Email); //
[email protected]
}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact
WHERE Email IN: emailSet AND ID NOT IN:newRecords ]){ // same
record
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}

ls
public static void
preventDuplicateContactBasedOnEmailBeforeUpdate(List<Contact>

oo
newRecords, Map<Id, Contact> oldRecords){
// Check the duplicate records based on Email
/*
* 0.1 - store the emails into a set

Email
ch
* 1. Get the Existing Records of Contact Object based on

* 2. Check if the record found


rS
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
for(Contact con : newRecords){
he

// Check if the Email is Changed


Contact oldRecord = oldRecords.get(con.Id);
// add the email to set
// [email protected] --> Old Email
nt

// [email protected] --> New/Updated Email


if(oldRecord.Email <> con.Email &&
String.isBlank(con.Email) == false){ //
Pa

[email protected]
emailSet.add(con.Email); //
[email protected]
} else if(String.isBlank(con.Email)) {
con.Email.addError('Email can not be blank!');
}
}
// 003dL000001iwXZQAY
Map<String, Contact> emailToContactMap = new Map<String,
Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact
WHERE Email IN: emailSet AND ID NOT IN:newRecords ]){ // same
record
// Email+Company
emailToContactMap.put(con.Email, con);
}
System.debug('emailToContactMap \n '+emailToContactMap);
checkDuplicate(newRecords, emailToContactMap);
}

public static void


preventDuplicateContactBasedOnEmail(List<Contact> newRecords){

ls
// Check the duplicate records based on Email
/*

oo
* 0.1 - store the emails into a set
* 1. Get the Existing Records of Contact Object based on
Email
* 2. Check if the record found
ch
* 3. Add Error
*/
Set<String> emailSet = new Set<String>();
rS
for(Contact con : newRecords){
// add the email to set
System.debug(' con.Email '+ con.Email);
System.debug(' con.LastName '+ con.LastName);
he

if(String.isBlank(con.Email) == false){
// null, ''
emailSet.add(con.Email);
} else {
nt

con.Email.addError('Email can not be blank!');


}
}
Pa

Map<String, Contact> emailToContactMap = new Map<String,


Contact>();
for(Contact con: [SELECT Id, Name, Email FROM Contact
WHERE Email IN: emailSet]){
emailToContactMap.put(con.Email, con);
}

checkDuplicate(newRecords, emailToContactMap);
// keyset() - Set of Keys
// values() - List of values
// get(key) -
// #1 - [email protected]
// does email found - No
// Add a new entry in the Map
// #2 - [email protected]
// does email found - Yes
}

@TestVisible
private static void checkDuplicate(List<Contact> newRecords,
Map<String, Contact> emailToContactMap){

ls
for(Contact con : newRecords ){
// #3 - Recommended

oo
// String key = Email+Company
if(emailToContactMap.containsKey(con.Email)){ //
[email protected]
con.addError('Duplicate contact found!
ch
'+con.Email);
con.Email.addError('Duplicate contact found!
'+con.Email);
rS
} else {
emailToContactMap.put(con.Email, con);
}
}
he

public static void handleAfterInsert(List<Contact>


newRecords){
nt

countContacts(newRecords);
}
Pa

public static void handleAfterUndelete(List<Contact>


newRecords){
countContacts(newRecords);
}

public static void handleAfterDelete(List<Contact>


oldRecords){
countContacts(oldRecords);
}

public static void handleAfterUpdate(List<Contact> newRecords,


Map<Id, Contact> oldRecordMap){
Map<Id, Account> accountMap = new Map<Id, Account>();
for(Contact con : newRecords){
Contact oldRecord = oldRecordMap.get(con.Id);
if(oldRecord.AccountId <> con.AccountId &&
con.AccountId != null){
accountMap.put(con.AccountId, new Account(Id =
con.AccountId, Number_of_Contacts__c = 0) );
if(oldRecord.AccountId <> null){
accountMap.put(oldRecord.AccountId, new
Account(Id = oldRecord.AccountId, Number_of_Contacts__c = 0) );

ls
}
}

oo
}
System.debug(accountMap);
List<AggregateResult> agrslt = [SELECT count(Id)
totalCount, count(Email) totalEmailCount, AccountId
ch FROM Contact
WHERE AccountId IN:
accountMap.keySet() Group By AccountId];
rS

for(AggregateResult agr: agrslt){

Integer totalContact = (Integer)agr.get('totalCount');


he

Id accountId = (Id)agr.get('AccountId');
Account acc = accountMap.get(accountId);
acc.Number_of_Contacts__c = totalContact;
accountMap.put(accountId, acc );
nt

}
Pa

update accountMap.values();
}

public static void countContacts(List<Contact> contactList){

Map<Id, Account> accountMap = new Map<Id, Account>();


for(Contact con: contactList){
if(con.AccountId != null){
accountMap.put(con.AccountId, new Account(Id =
con.AccountId, Number_of_Contacts__c = 0) );
}
}

System.debug(accountMap);
List<AggregateResult> agrslt = [SELECT count(Id),
count(Email), AccountId
FROM Contact
WHERE AccountId IN:
accountMap.keySet() Group By AccountId];

for(AggregateResult agr: agrslt){

ls
Integer totalContact = (Integer)agr.get('expr0');
Id accountId = (Id)agr.get('AccountId');

oo
Account acc = accountMap.get(accountId);
acc.Number_of_Contacts__c = totalContact;
accountMap.put(accountId, acc );

}
ch
try{
rS
update accountMap.values(); // List<Account>
}Catch(System.DMLException ex){
Logger.log(ex, 'ContactTriggerHelper',
'countContacts', UserInfo.getUserId(), 'ERROR', 'Apex Class',
he

'Error While Updating the Accounts Record');


}catch(System.Exception ex){
Logger.log(ex, 'ContactTriggerHelper',
'countContacts', UserInfo.getUserId(), 'ERROR', 'Apex Class',
nt

'Error While Updating the Accounts Record');


}
}
Pa

public static void exception(System.Exception ex){


System.debug(ex.getMessage());
System.debug(ex.getCause());
System.debug(ex.getStackTraceString());
System.debug(ex.getLineNumber());
}
}

public class ContactBatchClass implements


Database.Batchable<sObject>, Database.Stateful {
public Integer successRecords = 0;
public Integer failedRecords = 0;
public Integer totalRecords = 0;
public Database.QueryLocator start(Database.BatchableContext
batchContext){
System.debug('Start Executed');
// sObject Exception
return Database.getQueryLocator([SELECT Id, Name, Title,
Email FROM Contact]);
}

ls
public void execute(Database.BatchableContext batchContext,

oo
List<Contact> contactRecords){
totalRecords += contactRecords.size(); // totalRecords =
totalRecords + contactRecords.size();
System.debug(' totalRecords '+ totalRecords);
ch
for(Contact con: contactRecords){
con.Description = con.Email;
}
rS
/*try{
update contactRecords;
}catch(System.Exception ex){
Logger.log(ex, 'ContactBatchClass', 'execute',
he

UserInfo.getUserId(), 'ERROR', 'Apex Class', 'Error While Updating


the Contact Record');
}*/
System.debug('Hello');
nt

System.debug('Hello');
System.debug('Hello');
List<Database.SaveResult> results =
Pa

Database.update(contactRecords, false); //Partial DML


List<ErrorLog__c> errorLogList = new List<ErrorLog__c>();
for(Database.SaveResult sr: results){
if(sr.IsSuccess()){
successRecords += 1;
}else{
failedRecords += 1;
List<Database.Error> error = sr.getErrors();
ErrorLog__c log = Logger.log(String.join(error, '
: ') +' LINE No : 33 ', 'ContactBatchClass', 'execute',
UserInfo.getUserId(), 'ERROR', 'Apex Class', 'Error While Updating
the Contact Record');
errorLogList.add(log);
}
}
Database.insert(errorLogList, false);
System.debug(' successRecords '+ successRecords);
System.debug(' failedRecords '+ failedRecords);
}

public void finish(Database.BatchableContext batchContext){


System.debug('Finish Executed');

ls
System.debug(' totalRecords in finish Method '+
totalRecords);

oo
System.debug(' successRecords in finish Method '+
successRecords);
System.debug(' failedRecords in finish Method '+
failedRecords);
}
ch
}
rS

public class OpportunityException extends Exception {}


he

public class DebugUtil {

public static void debug(Object message){


nt

System.debug(message);
}
}
Pa

OpportunityException myException = new OpportunityException();


myException.setMessage('Opportunity Amount can not be less than $10K');

try{
throw new OpportunityException('Opportunity Amount can not be less
than $10K');
}catch(System.Exception ex){
if(Boolean.ValueOf(System.Label.USE_DEBUG))
DebugUtil.debug(ex.getMessage());
if(Boolean.ValueOf(System.Label.USE_DEBUG))
DebugUtil.debug(ex.getCause());
if(Boolean.ValueOf(System.Label.USE_DEBUG))
DebugUtil.debug(ex.getStackTraceString());
}

Resources

ls
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_classes_exception_methods.htm?q=DmlException

oo
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_classes_exception_methods.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex


ch
_methods_system_string.htm#apex_System_String_split
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_exception_definition.htm
rS

Day 62 - Basic Apex trigger framework in Salesforce


he

Recording Link
https://youtu.be/zEiDBdTalBU
nt

Introduction

An Apex Trigger framework in Salesforce is essential for building robust,


Pa

maintainable, and scalable applications. Here are the key reasons why an Apex
Trigger framework is needed:

● Code Organization and Maintainability


● Reusability
● Consistency
● Simplified Testing
● Order of Execution
● Centralised Error Handling
● Scalability
Prerequisite

Create Custom Metadata Object & Record

Create a Custom Metadata with the label “sObject Trigger Setting” and plural label
“sObject Trigger Settings” with the following fields

1. Is Active - Checkbox
2. Object Name - Metadata Relationship(Entity Definition)
3. Handler Class Name - Text(255)
4. Before Insert → Checkbox
5. After insert → Checkbox

ls
6. Before Update → Checkbox
7. After Update → Checkbox

oo
8. Before Delete → Checkbox
9. After Delete → Checkbox
10. After Undelete → Checkbox
11. Order → Number(4, 0)
ch
rS
he
nt
Pa
Approach to Develop Trigger Framework

Step 1: Define a Trigger Handler Interface

Create an interface to define the contract for all trigger handler classes.

public interface TriggerHandler {


void beforeInsert();
void afterInsert();
void beforeUpdate();
void afterUpdate();

ls
void beforeDelete();
void afterDelete();
void afterUndelete();

oo
}

Step 2: Create a Base Trigger Handler Class


ch
Implement a base class that provides default implementations for the interface
methods. This allows you to override only the methods you need in your specific
handlers.
rS

public abstract class BaseTriggerHandler implements TriggerHandler {


Public virtual void beforeInsert() {}
Public virtual void afterInsert() {}
he

public virtual void beforeUpdate() {}


public virtual void afterUpdate() {}
public virtual void beforeDelete() {}
public virtual void afterDelete() {}
nt

public virtual void afterUndelete() {}


}
Pa

Step 3: Create a Base Trigger Dispatcher Class

Create the generic class which will be used to handle the routing to the correct
method for the specific event in the Trigger.

public class BaseTriggerDispatcher {

public static void run(String objectName){


List<sObjectTriggerSetting__mdt> settings = [SELECT
HandlerClassName__c, Before_Insert__c, After_Insert__c,
Before_Update__c, After_Update__c
FROM sObjectTriggerSetting__mdt WHERE
ObjectName__r.QualifiedApiName =: objectName AND IsActive__c = True];
if(settings?.size() > 0){
sObjectTriggerSetting__mdt setting = settings.get(0);
dispatch(setting);
}
}

public static void dispatch(sObjectTriggerSetting__mdt setting){


BaseTriggerHandler handler =

ls
(BaseTriggerHandler)Type.forName(setting.HandlerClassName__c).newInstanc
e();
if(Trigger.isBefore){

oo
if(setting.Before_Insert__c){
handler.beforeInsert();
}
}else if(Trigger.isAfter){
ch if(setting.After_Insert__c){

}
handler.afterInsert();

}
rS
}
}

Step 4: Implement Specific Trigger Handlers


he

Create specific handler classes that extend the base handler class and override
the necessary methods.

public class AccountTriggerHandler extends BaseTriggerHandler {


nt

public void beforeInsert() {


// Custom logic for before insert
}
Pa

public void afterInsert() {


// Custom logic for after insert
}

// Override other methods as needed


}
Step 5: Implement the Trigger

trigger AssetTrigger on Asset (before insert, after insert, before


update, after update, before delete, after delete, after undelete) {
BaseTriggerDispatcher.dispatch('Asset');
}

Code Used in the Session

ls
TriggerInterface

oo
public interface TriggerInterface {
void handleBeforeInsert();
void handleAfterInsert();
void handleBeforeUpdate();
ch
void handleAfterUpdate();
void handleBeforeDelete();
void handleAfterDelete();
void handleAfterUndelete();
rS
}

BaseTriggerHandler
he

public abstract class BaseTriggerHandler implements


TriggerInterface {
nt

public virtual void handleBeforeInsert(){}


public virtual void handleAfterInsert(){}
public virtual void handleBeforeUpdate(){}
Pa

public virtual void handleAfterUpdate(){}


public virtual void handleBeforeDelete(){}
public virtual void handleAfterDelete(){}
public virtual void handleAfterUndelete(){}
}

BaseTriggerDispatcher

public class BaseTriggerDispatcher {


public static void dispatch(String objectApiName){
// SOQL Query on Custom Metadata
// Find the Entry for the Object
// If Entry is find dispatch to correct handlers
// If not do nothing
// Example - objectApiName = 'Asset'
List<sObjectTriggerSetting__mdt> settings = [SELECT
IsActive__c, HandlerClassName__c, ObjectName__r.QualifiedApiName,

Before_Insert__c, After_Insert__c, Before_Update__c,


After_Update__c,

ls
Before_Delete__c, After_Delete__c, After_UnDelete__c, Order__c

oo
FROM

sObjectTriggerSetting__mdt
WHERE
ch
ObjectName__r.QualifiedApiName =:objectApiName
ORDER BY
rS
Order__c ASC
];
for(sObjectTriggerSetting__mdt setting: settings){
if(setting.IsActive__c){
he

// Trigger is Active
dispatch( setting );
}
}
nt

/*if(settings?.size() > 0){


sObjectTriggerSetting__mdt setting = settings.get(0);
if(setting.IsActive__c){
Pa

// Trigger is Active
dispatch( setting );
}
}*/
}

private static void dispatch(sObjectTriggerSetting__mdt


setting){
// dipatch to the correct trigger handler
// HandlerClassName__c = 'AssetTriggerHandler';
// Object
BaseTriggerHandler handler =
(BaseTriggerHandler)Type.forName(setting.HandlerClassName__c).newI
nstance();

if(Trigger.IsBefore){
if(Trigger.isInsert && setting.Before_Insert__c){
handler.handleBeforeInsert();
} else if(Trigger.isUpdate &&
setting.Before_Update__c){
handler.handleBeforeUpdate();
} else if(Trigger.isDelete &&

ls
setting.Before_Delete__c){
handler.handleBeforeDelete();

oo
}
}else if(Trigger.isAfter){
if(Trigger.isInsert && setting.After_Insert__c){
handler.handleAfterInsert();
ch } else if(Trigger.isUpdate &&
setting.After_Update__c){
handler.handleAfterUpdate();
rS
} else if(Trigger.isDelete &&
setting.After_Delete__c){
handler.handleAfterDelete();
} else if(Trigger.isUnDelete &&
he

setting.After_UnDelete__c){
handler.handleAfterUnDelete();
}
}
nt

}
}
Pa

AssetTrigger

trigger AssetTrigger on Asset (before insert, after insert, before


update, after update, before delete, after delete, after undelete)
{
BaseTriggerDispatcher.dispatch('Asset');
}

AssetTriggerHandler
public class AssetTriggerHandler extends BaseTriggerHandler {

public override void handleBeforeInsert(){


DebugUtil.debug('AssetTriggerHandler handleBeforeInsert
');
}

public override void handleAfterInsert(){


DebugUtil.debug('AssetTriggerHandler handleAfterInsert ');
}

ls
public override void handleBeforeUpdate(){

oo
DebugUtil.debug('AssetTriggerHandler handleBeforeUpdate
');
}
}
ch
AssetTriggerPostHandler
rS
public class AssetTriggerPostHandler extends BaseTriggerHandler {

public override void handleAfterInsert(){


DebugUtil.debug('AssetTriggerPostHandler ');
he

DebugUtil.debug(Trigger.New);
}
nt

}
Pa

Resources

● Type Class in Salesforce


● Trigger Framework 1
● Advanced Trigger Framework
Day 63 - How to Develop your code securely in
Salesforce

Recording Link
https://youtu.be/7R8pLjnNefM

Secure DML in Apex

ls
// User Context

Account acc = new Account(Name = 'TEST ABC');

oo
insert as user acc;
Database.insert(acc, false, ACCESSLEVEL.USER_MODE);

ch
// System Context

insert as system acc;


Database.insert(acc, false, ACCESSLEVEL.SYSTEM_MODE);
rS

Secure Queries in Salesforce Apex


he

All the Apex Queries run in the System mode by default which means developers
can play around with the security and can use the available techniques for
nt

applying the Security.


Pa

Security in SOQL Query in Apex

● WITH SECURITY_ENFORCED
● WITH USER_MODE
● WITH SYSTEM_MODE
● AccessLevel

WITH SECURITY_ENFORCED
SECURITY_ENFORCED keyword enforces the field-level & object-level security that
are being used on the SOQL Query.

Note: -

● The Security will only be enforced for the fields that are part of the SELECT
Query.
● The permission for the fields on the WHERE Part or other parts of the Query
will not be checked.
● WITH SECURITY_ENFORCED is not supported with SOSL

ls
● Throws the System.QueryException if the user does not have access to
fields & objects. But the exception will not tell the object name or field API

oo
name.
● WITH USER_MODE performs better than WITH SECURITY_ENFORCED

ch
List<Course__c> courseList = [SELECT Id, Name, StartDate__c, Price__c
FROM Course__c
rS
WITH SECURITY_ENFORCED
];
he

In the above query, the salesforce will do the following

● Read Permission on Course__c object for the logged-in user profile.


nt

● Read Permission for StartDate__c & Prices__c, Name, Id for the logged-in
user profile on the Course__c object.
● Record-level access (like org-wide defaults and sharing rules) on the
Pa

Course__c objects for the user

WITH USER_MODE
USER_MODE keyword enforces the field-level & object-level security that is being
used on the SOQL & SOSL Query.

Note:
● The Security will be enforced for fields used in the where clause, SOQL
QUERY, and order by or fields used in the relationship query or polymorphic
lookup
● WITH USER_MODE is supported with SOSL & Polymorphic query as well.
● Throws the System.QueryException if the user does not have access to
fields & objects. And also gives us the details about all the fields to which
the user does not have access. Use getInaccessibleFields() method to get
the details

ls
List<Course__c> courseList = [SELECT Id, Name, StartDate__c, Price__c

oo
FROM Course__c
WITH USER_MODE
];
ch
In the above query, the salesforce will do the following

● Read Permission on Course__c object for the logged-in user profile.


rS

● Read Permission for StartDate__c & Prices__c for the logged-in user profile
on the Course__c object.
● Record-level access (like org-wide defaults and sharing rules) on the
he

Course__c objects for the user

Working with Database Query


nt

String query = 'SELECT ID, Name, Industry FROM Account LIMIT 1';
List<Account> accountList = Database.query(query, AccessLevel.USER_MODE);
Pa

String query = 'SELECT ID, Name, Industry FROM Account LIMIT 1';
List<Account> accountList = Database.query(query, AccessLevel.SYSTEM_MODE);

Note:- AccessLevel.SYSTEM_MODE does not work in the Anonymous window


WITH USER_MODE vs WITH SECURITY_ENFORCED

WITH USER_MODE WITH SECURITY_ENFORCED

Supported in both SOQL & SOSL. Supports Supported only in SOQL


Polymorphic Query

The Exception gives the details about the No field information is given
fields

ls
Performs better than WITH NA
SECURITY_ENFORCED

oo
Can be used in Database.query Method with Can not be used
AccessLevel Class
ch
Can use AccessLevel Class with
Database.Query Method
Can not use AccessLevel Class
with Database.Query Method
rS
Handles CRUD/FLS for fields used in the Handles CRUD/FLS for fields
where clause and order by or fields used in before FROM Clause
the relationship query or polymorphic lookup
he

Recommended by Salesforce NA

Code used in the Session


nt

public class CourseHelperClass {


Pa

public static void run(){


Course__c cs = new Course__c();
cs.Name = 'Salesforce Deployment';
cs.StartDate__c = System.today();
cs.Price__c = 24444.5;
cs.Status__c = 'Published';
cs.Discount__c = 20;

insert cs;

Course__c csr = [SELECT StartDate__c, EndDate__c, Price__c,


Name, Status__c, Id FROM Course__c WHERE Id =: cs.Id ];
System.debug(csr);
}

public static void runAsUser(){


Course__c cs = new Course__c();
cs.Name = 'Salesforce Deployment';
cs.StartDate__c = System.today();
cs.Price__c = 24444.5;
cs.Status__c = 'Published';
cs.Discount__c = 20;

ls
try{
insert as user cs; // 1 record --> index 0
}catch(System.DMLException ex){

oo
DebugUtil.debug(ex.getMessage());
DebugUtil.debug(ex.getCause());
DebugUtil.debug(ex.getDmlFieldNames(0));
DebugUtil.debug(ex.getDmlFields(0));
ch
}catch(System.Exception ex){

}
rS
Database.SaveResult sr = Database.insert(cs, false,
AccessLevel.USER_MODE);
DebugUtil.debug(sr.getErrors());
he

List<Course__c> courseList;
try{
/*List<Course__c> courseList = [SELECT StartDate__c,
EndDate__c,
nt

Price__c, Discount__c, Name,


Status__c, Id
FROM
Course__c
Pa

WHERE
Status__c = 'Draft'
WITH SECURITY_ENFORCED // NOT
RECOMMENDED
];
DebugUtil.debug(courseList);*/
courseList = [SELECT StartDate__c, EndDate__c,
Price__c, Discount__c, Name,
Status__c, Id
FROM
Course__c
WHERE
Status__c = 'Draft'
WITH SYSTEM_MODE //RECOMMENDED
ORDER BY Status__c DESC
];
DebugUtil.debug(courseList);
}catch(System.QueryException ex){
DebugUtil.debug('System.QueryException Executed);
DebugUtil.debug(ex.getMessage());
DebugUtil.debug(ex.getInaccessibleFields());
// Logging Framework
}catch(System.Exception ex){

ls
DebugUtil.debug(ex.getMessage());
// Logging Framework
}

oo
DebugUtil.debug(courseList);
if(courseList?.size()>0){
// Do business Logic
}
} ch
public static void runQuery(String name){
String query = 'SELECT Id, Name FROM Contact WHERE Name =:name';
rS
Map<String, Object> bindParams = new Map<String, Object>();
bindParams.put('name', name);
bindParams.put('email', '[email protected]');
runDatabaseQuery(query, bindParams);
he

public static void runDatabaseQuery(String query, Map<String,


Object> bindParams){
nt

List<Contact> contactList = Database.queryWithBinds(query,


bindParams, AccessLevel.User_MODE);
System.debug(contactList);
// Perform some business logic
Pa

}
}

Resources

● https://developer.salesforce.com/blogs/2023/05/write-simplified-and-secure
-apex-with-spring-23-updates
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_class_System_AccessLevel.htm
● https://salesforce.stackexchange.com/questions/16121/sfdc-understanding-w
ith-sharing-without-sharing-unspecified-sharing-classes

Day 64 - With sharing vs Without sharing keyword in


Apex Class

Recording Link
https://youtu.be/4Ox72Z8FE0g

ls
Introduction

oo
All the Apex Queries run in the System mode by default which means developers
can play around with the security and can use the available techniques for
applying and forging the Security.
ch
The default keyword on the Apex Class is without sharing
rS
with sharing keyword implies in other words, "with Security Settings enforced".
The sharing only respects OWDs and Sharing Rules and any other sharing like
Manual Sharing, Apex Managed Sharing etc.
he

What about Object Level Security/OLS and Field Level Security/FLS? because
sharing does not enforce any object-level or field-level security
nt

With Sharing keyword


Pa

public with sharing class SOQLUtil {


}

Without sharing
Working for experience cloud
→ You have UI to onboard a New User from Experience Cloud
→ Experience cloud user can not create a User
→ Account, Contact & User is getting created.
→ with sharing for experience cloud users

public without sharing class SOQLUtil {


}

Different Combination of the sharing keyword

Let’s assume that we have three different classes with the following declaration

ls
oo
public with sharing class UserUtility { // Class A
public static void methodA(){}
}
ch
public without sharing class UserUtility { // Class A
public static void methodA(){}
rS
}
he

public without sharing class AccountUtility { // Class B


public static void methodA(){ UserUtility.methodA(); }
}
nt

public with sharing class AccountUtility { // Class B


public static void methodA(){ UserUtility.methodA(); }
Pa

public class SOQLUtil { // without sharing


}
Scenarios

→ class AccountUtility extends UserUtility => The code of class AccountUtility will
now execute in class UserUtility's mode, that is "with sharing" mode. Because the
UserUtility class is the parent class.

→ class AccountUtility calls class UserUtility => called code [ UserUtility ] will now

ls
be executed in the with sharing mode because it is declared with the with sharing
keyword that is “with sharing”

oo
→ class SOQLUtil calls class UserUtility => called method's code [ UserUtility ] will
execute in class UserUtility's mode, that is with sharing
ch
→ class SOQLUtil extends class UserUtility => code in class SOQLUtil now
executes in the parent class UserUtility’s mode, That is "with sharing" mode.
rS

→ class UserUtility calls SOQLUtil => code in class SOQLUtil is executed in class
UserUtility's mode, i.e. with sharing mode
he

→ class UserUtility extends SOQLUtil => class UserUtility's code now executes in
the parent class SOQLUtil's mode, i.e. in "without sharing" mode.
nt

Resources
Pa

● https://salesforce.stackexchange.com/questions/16121/sfdc-understanding-w
ith-sharing-without-sharing-unspecified-sharing-classes
Day 65 - Governor limits in Salesforce

Recording Link

https://youtu.be/V2FcLSOfVks

SOQL 101
This error comes into the picture when you are making more than 100 SOQL
queries in a single transaction. As Salesforce runs on Multi-Tenant Architecture

ls
you can not get any higher limit for the SOQL.

How to Resolve SOQL 101 error -

oo
1. Always try to use One Query for a Single Object to minimise the SOQL Query
and use a for loop to prepare the list if needed.
ch
2. Use the @future method to run some piece of code in Async mode
3. Avoid SOQL Queries inside for loop
4. Follow the Salesforce best practices for Bulk Logic
rS
5. Use Limits class and be aware of your LIMITS

System.LimitException: Too many DML statements


he

As per Salesforce, we can only make 150 DML in a single transaction, and if we
exceed more than 150 DML then System.LimitException: Too many DML
statements exceptions would occur.
nt

How to Resolve System.LimitException: Too many DML statements: 151


Pa

1. Avoid DML inside for loop


a. Prepare a List and add all the records in the list of the Same type
then do the DML on the list
2. Use Limits class and be aware of your LIMITS
a. Limits.getDMLStatements();
b. Limits.getLIMITDMLStatements(); // 150

Too Many DML Rows: 10001


Total number of records processed as a result of DML statements,
Approval.process, or database.empty recycle bin: 10,000.
● getLimitDMLRows() // 10000
● getDMLRows()

Too Many Query Rows : 50001


The errors come into the picture when your SOQL queries are returning more than
50K records in a Single transaction.

● Apply Maximum filters to get the selective records


● Always use LIMIT Statement in SOQL Query
● Always use LIMITS Class

ls
○ getLimitQueryRows()
○ getQueryRows()

oo
CPU Timeout Limit Exceeded
Any sync process should get completed within 10 seconds and any async process
ch
should complete within 60 seconds if the process takes more than 10 & 60
seconds then a CPU Timeout error will come into the picture.
rS
What is not counted for Apex CPU Time Limit?

● Database operations like database operation for DML, SOQL, and SOSL.
● Wait time for Apex Callout.
he

● Aggregate SOQL.

How to resolve CPU Timeout Error in Salesforce.


nt

1. Write one Trigger per object.


2. Avoid using Process Builder
Pa

3. Use one flow per object and per event.


a. Account - Created Update Industry
b. Account - Updated
c. Account - deleted
d. Account - Create Update Rating
4. Try to Avoid nested for loop
a. With the help of Map
5. Stop Recursion in Apex Code.
6. Avoid multiple Validation Rules
7. Using Map based query
a. keySet
b. values
8. Use Async Apex if Applicable
9. Aggregate SOQL usage
a. Count / SUM / Avg / Min / MAX
10. For more information
a. https://www.apexhours.com/apex-cpu-time-limit-exceeded/

The apex heap size is too large

ls
Salesforce has predefined limits for both the Sync and Async process that
enforces an Apex Heap Size Limit of 6MB for synchronous transactions and 12MB

oo
for asynchronous transactions.

Best practices for using the Heap Size Issue


ch
Avoid using the Class level variables to store a large amount of data like a
List or Map
● Clear the variable or set the value as null after the work for the variable has
rS
been completed and the variable is no longer needed
● Use the SOQL for loop wherever applicable
● Use of Map to avoid the nested for loops
he

● Use static & Transient variables


○ Use Multiple classes process wise
nt

Callout Limit

● Total no of Callouts is 100 in a Single transaction and in case of batch apex


100 callouts from each method.
Pa

SOQL Record LIMIT

● Total no records returned by SOSL Query - 2K records

SOSL Query LIMIT

● Total number of SOSL queries in Single Transaction - 20


Code Used in the Session

public with sharing class UserHelper {

public static void query(List<Contact> contactList){

Set<Id> accountIdsSet = new Set<Id>();


for(Contact con: contactList){
accountIdsSet.add(con.AccountId);
}

ls
Map<Id, Account> accountMap = new Map<Id, Account>(
[Select Id, Name, Industry, Rating FROM Account WHERE Id IN:
accountIdsSet]

oo
);
for(Contact con: contactList){ // Any Child Object
Account acc = accountMap.get(con.AccountId);

}
}ch
public static void dml(){
System.debug(' getDMLStatements() '+ Limits.getDMLStatements()
rS
);
System.debug(' getLIMITDMLStatements '+
Limits.getLIMITDMLStatements() );
List<Account> accList = new List<Account>();
for(Integer i = 0; i < 154; i++){
he

Account acc = new Account(Name = 'Limit '+i);


accList.add(acc);
}
insert accList;
nt

System.debug(' getDMLStatements() '+ Limits.getDMLStatements()


);
System.debug(' getLIMITDMLStatements '+
Pa

Limits.getLIMITDMLStatements() );
}

public static void query(){


System.debug(' getLimitQueryRows() '+ Limits.getLimitQueryRows()
);
System.debug(' getQueryRows '+ Limits.getQueryRows() );
Map<Id, Account> accountMap = new Map<Id, Account>(
[Select Id, Name, Industry, Rating FROM Account ]
);
System.debug(' accountMap '+ accountMap.size() );
Map<Id, Contact> contactMap = new Map<Id, Contact>(
[Select Id, Name FROM Contact ]
);
System.debug(' contactMap '+ contactMap.size() );
Map<Id, Task> taskMap = new Map<Id, Task>(
[Select Id FROM Task ]
);
System.debug(' taskMap '+ taskMap.size() );

System.debug(' getLimitQueryRows() '+ Limits.getLimitQueryRows()


);
System.debug(' getQueryRows '+ Limits.getQueryRows() );

ls
}

public static void mapx(List<Contact> contactList){

oo
Set<Id> accountIdsSet = new Set<Id>();
for(Contact con: contactList){
accountIdsSet.add(con.AccountId);
}
ch
List<Account> accList = [Select Id, Name, Industry, Rating FROM
Account WHERE Id IN: accountIdsSet];
Map<Id, Account> accountMap = new Map<Id, Account>();
for(Account acc: accList){
rS
accountMap.put(acc.Id+'_'+acc.Industry, acc);
}
for(Contact con: contactList){
String key = con.AccountId+'_'+con.Industry__c;
he

if(accountMap.containsKey(key)){
Account acc = accountMap.get(key);
}
/*for(Account acc: accList){
nt

if(con.AccountId == acc.Id && acc.Industry ==


con.Industry__c){
// logic here
}
Pa

}*/
}
}

public static void heap(Set<Id> contactIdsSet){


String randomString =
'bhlahbhlahbhlahbhlahbhlahbhlahbhlahbhlahbhlahbhlahbhlahbhlahbhlah';
List<Account> accList = [Select Id, Name, Industry, Rating FROM
Account ];
for(Account acc: accList){
// Business Logic
}
update accList; // Work is Done
accList.clear();
randomString = null;
//SOQL -- DML

//List<Contact> contactList = [SELECT Id, Name, EMAIL FROm


COntact WHERE Id IN : contactIdsSet]; // BAD PRACTICE
Set<String> contactEmailSet = new Set<String>();
for(Contact con: [SELECT Id, Name, EMAIL FROm COntact WHERE Id
IN : contactIdsSet]){
contactEmailSet.add(con.Email);

ls
}

oo
}

Resourcesch
● https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/
apex_gov_limits.htm
rS
● https://www.apexhours.com/apex-cpu-time-limit-exceeded/
● https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_che
atsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platf
he

orm_apexgov.htm
● https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex
_methods_system_limits.htm
nt
Pa
DAY 66 - Best Practices for writing your Apex Code

Recording Link
https://youtu.be/fP8CstbvslU

Avoid DML inside For Loop - LIMIT 150 in a Single Transaction


Avoid SOQL Queries inside For Loop - LIMIT 100 in a Single Transaction
Utilise SOQL For Loop
Utilise Map based SOQL Queries

ls
Use One Trigger per Object per Event

● Order of Execution will be in Control

oo
● Easy to maintain
● Easy to Debug

ch
Develop Logic Less Apex Trigger

Write the meaning ful test cases


rS

Use runAs in Test Classes

Never use Dummy Code Coverage


he

Never Use Existing Data for Code coverage

● SeeAll data = true


nt

Use Database Methods for DML


https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_meth
Pa

ods_system_database.htm

Querying Large Data Sets

Account - 20M records


→ like, not like – ANI-PATTERN [PERFORMANCE YOUR SOQL]
SELECT Id, Name FROM Account WHERE Name not like ‘%sf%’
→ Add More filters, Limit statement, =
SELECT Id, Name FROM Account WHERE Name = ‘Salesforce.com’
→ Use filters on Indexed field

Use Of The Limits Apex Class Methods

https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_meth
ods_system_limits.htm

Exception handling in Apex Code

Use Asynchronous Apex

ls
● future
● Queueable Apex

oo
● Batch Apex
● Scheduled Apex

ch
Try to Avoid Nesting For Loops

Balance the Clicks and Code solutions


rS
● Can we do this using admin tools ?
○ Flow
○ Formula
he

○ Validation Rules
○ Page Layouts
○ Record Types
nt

○ Sharing Rules
○ Custom Objects
● What is max records that we need to process
Pa

○ Min 200 Records

Use Apex Trigger Framework to control the Apex Trigger Execution

Naming Conventions

1. Code Review & Checklist Process


2. Develop reusable Code
Proper Comments

Type Class

Schema Class → Advanced Development

DAY 67 - How to Debug Your Apex Code with Debug Logs & Developer
Console

ls
Recording Link
https://youtu.be/B620uVxF2Z0

oo
Debug Log Categories

● Database
ch
● Workflow
● NBA
● Validation
rS

● Callout
● Apex Code
● Apex profiling
he

● Visualforce
● System
nt

Debug Log Levels

● NONE
● ERROR
Pa

● WARN
● INFO
● DEBUG
● FINE
● FINER
● FINEST

Resources

● Salesforce Help Document


DAY 68 - Record Sharing Using Apex

Recording Link
https://youtu.be/_H1tbKB4sOA

ls
Introduction

oo
To share a record with the User or Group of Users using the Program we work with
Apex Managed Sharing. Every Standard Object will have its Own Share Object.

ch
For Example, Account will have AccountShare, Contact will have ContactShare,
Lead will have LeadShare, and Case will have CaseShare.
rS
For any custom object, the Share object will become available only and only if the
OWD for the Object is either public read-only or Private.

The Share object for the Custom Object can be constructed using the following
he

combination

Object API Name excluding __c + __share


nt

For example, the Object API Name is


Pa

● AccountRequest__c then the share object will be AccountRequest__share


● Booking_Item__c then the share object will be Booking_Item__share
● InvoiceLine_Item__c then the share object will be InvoiceLine_Item__share

Note: - Any object that is on the child side in case of a Master-Detail relationship
will not have its own share object.

Any Share Object for the custom object will have the following fields
● objectNameAccessLevel - The Level of Access to be granted. Valid values
are
○ Read
○ Edit
○ All
● ParentId - The Id of the custom object record to be shared
● RowCause - The reason why the user or group is being granted access. By
default “Manual” will be added for any manual share.
● UserOrGroupId - The user or group IDs to which you are granting access. A

ls
group can be:
○ A public group or a sharing group associated with a role

oo
○ A territory group

ch
Course__Share share = new Course__Share();
share.AccessLevel = 'Read'; // Read Only
share.ParentId = 'a02dL000003a3LpQAI';
rS
share.UserOrGroupId = '005dL000003cuF3QAI';
share.RowCause = 'Manual';
insert share;
he

SELECT Id, AccessLevel, ParentId, RowCause, UserOrGroupId


nt

FROM
DeletedOpportunity__share
WHERE
Pa

ParentId = 'a062w00000ZHKQTAA5'

Select Id, OpportunityId, UserOrGroupId, OpportunityAccessLevel, RowCause


FROM
OpportunityShare
WHERE
OpportunityId = '0062w00000MoUv2AAF'
AND RowCause = 'Manual'

Custom Apex Managed Sharing Reason

Custom Apex Managed Sharing helps the business to control the Sharing Reason
and can distinguish why the record has been shared with the user or group of

ls
users.

oo
Note:- Custom Apex Managed Sharing is only available for the Custom Object.

If we share the record with Custom Sharing reason the sharing will not be lost
ch
even if the record owner is getting changed.

To create the Custom Apex Managed Sharing → Navigate to Classic → Setup →


rS
Objects → Go to any custom Object Scroll the Apex Sharing Reason and create
your sharing reasons there.
he
nt
Pa
DAY 69 - Order of Execution in Salesforce

Recording Link
https://youtu.be/SohnM60Dzjk

Link to the Document -

ls
oo
ch
rS
he

Lead Record is inserted → After Insert Trigger → Task Insert [Order of Execution for
Task Record ] → Resume the Order of Execution for Lead
nt

Schema Class in Salesforce


Pa

System
→ Schema
→ Methods
→ Classes

Namespace - System
Class Name - Schema
Purpose - Read the Schema of the Salesforce Org
Some useful methods

● getGlobalDescribe - return type is → Map<String,


Schema.SObjectType>
● describeSObjects - return type is →
List<Schema.DescribeSObjectResult>

SObjectType Class in Salesforce


Namespace - System

ls
Class Name - SObjectType
Purpose - Useful to get the Information about the object and then fetching

oo
the field details.

ch
Some useful methods

● getDescribe - return type is → Schema.DescribeSObjectResult


rS

DescribeFieldResult Class in Salesforce


Namespace - System
he

Class Name - SObjectType


Purpose - Useful to get the Information about the fields like formula and
nt

picklist values.
Pa

Some useful methods

● getDescribe - return type is → Schema.DescribeSObjectResult

public class PS_Schema {

/*1. List All the Objects*/


public static void listObjects(){
Map<String, Schema.SObjectType> objectKeyMap =
Schema.getGlobalDescribe();
System.debug(objectKeyMap);
}
/* 2. Get The information about the Object*/
public static void describeObject(String objectApiName){
Map<String, Schema.SObjectType> objectKeyMap =
Schema.getGlobalDescribe();
Schema.SObjectType objectType =
objectKeyMap.get(objectApiName);
System.debug(objectType);
}

ls
/* 3. Get The Complete information about the Object*/
public static void getComleteInformationAboutTheObject(String

oo
objectApiName){
Map<String, Schema.SObjectType> objectKeyMap =
Schema.getGlobalDescribe();
Schema.SObjectType objectType =
ch
objectKeyMap.get(objectApiName);
Schema.DescribeSObjectResult result =
objectType.getDescribe();
rS
DebugUtil.debug(result.getLabel());
DebugUtil.debug(result.getName());
DebugUtil.debug(result.getKeyPrefix());
he

DebugUtil.debug(result.getRecordTypeInfos());
DebugUtil.debug(result.getRecordTypeInfosById());

DebugUtil.debug(result.getRecordTypeInfosByName().get('Business').
nt

getRecordTypeId());

DebugUtil.debug(result.getRecordTypeInfosByDeveloperName().get('In
Pa

dividual').getRecordTypeId() );

DebugUtil.debug(result.getChildRelationships()); // Parent
Relationship

Map<String, Schema.SObjectField> fields =


result.fields.getMap();
DebugUtil.debug( fields );
}

/* 4. Get The Complete information about the Field*/


public static void getComleteInformationAboutTheField(String
objectApiName, String fieldApiName){
Map<String, Schema.SObjectType> objectKeyMap =
Schema.getGlobalDescribe();
Schema.SObjectType objectType =
objectKeyMap.get(objectApiName);
Schema.DescribeSObjectResult result =
objectType.getDescribe();

Map<String, Schema.SObjectField> fieldsMap =


result.fields.getMap();

ls
DebugUtil.debug( fieldsMap );

oo
Schema.SObjectField field = fieldsMap.get(fieldApiName);
Schema.DescribeFieldResult fieldResult =
field.getDescribe();
}
ch
/* 5. Get The Picklist values for the field*/
public static void getPicklistValuesForTheField(String
rS
objectApiName, String fieldApiName){
Map<String, Schema.SObjectType> objectKeyMap =
Schema.getGlobalDescribe();
Schema.SObjectType objectType =
he

objectKeyMap.get(objectApiName);
Schema.DescribeSObjectResult result =
objectType.getDescribe();
nt

Map<String, Schema.SObjectField> fieldsMap =


result.fields.getMap();
DebugUtil.debug( fieldsMap );
Pa

Schema.SObjectField field = fieldsMap.get(fieldApiName);


Schema.DescribeFieldResult fieldResult =
field.getDescribe();

List<Schema.PicklistEntry> picklistEntries =
fieldResult.getPicklistValues();
for( Schema.PicklistEntry picklist : picklistEntries){
if(picklist.isActive()){
System.debug('Label is : '+ picklist.getLabel() +'
Value is : '+picklist.getValue());
}
}

// Schema.DescribeFieldResult fld =
Schema.getGlobalDescribe().get(objectApiName).getDescribe().fields
.getMap().get(fieldApiName).getDescribe();

ls
Resources

oo
● Salesforce Developer Document
● Salesforce Ben Blog
● The Complete Overview - Salesforce Image
ch
● Schema Class Salesforce
● SObjectType Class
rS
● Describe SObject Result
● Record Type Info Class
● Describe Field Result
● Picklist Entry
he
nt
Pa

You might also like