Monday 24 May 2021

Real-time scenarios

 Scenario based Salesforce interview questions. 

1. Batch apex syntax. 

An asynchronous process is a process or function that executes a task "in the background" without the user having to wait for the task to finish. 

Batch Apex is used to run large jobs (think thousands or millions of records!) that would exceed normal processing limits.


global class SampleBatch implements Database.Batchable<sObject> {

    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(/* SOQL query*/);
    }
    global void execute(Database.BatchableContext bc, List <Account> listAccount) {
        //logic
    }
    global void finish(Database.BatchableContext bc) {
    }
}


Batch Apex to delete Accounts which are 7 days old in Salesforce.

global class deleteOldRecordsBatch implements Database.Batchable<sObject> {
    global Database.QueryLocator start(Database.BatchableContext bc) {
        Date sevenDaysBefore = System.today().addDays(-7);
        String SOQL = 'SELECT Id FROM Account WHERE CreatedDate = : sevenDaysBefore';
        return Database.getQueryLocator(SOQL);
    }
    global void execute(Database.BatchableContext bc, List <Account> listAccount) {
        delete listAccount;
    }
    global void finish(Database.BatchableContext bc) {
    }
}


2. Schedulable apex syntax: to run Apex classes at a specified time


global class SampleSchedulableClass implements Schedulable{

    global void execute(SchedulableContext ctx){
        SampleBatch batch = new SampleBatch();
        Database.executebatch(batch, 200);
    }

3. Future method syntax: 

Future Apex is used to run processes in a separate thread, at a later time when system resources become available.

public class sampleFuture{
    
@future
    public static void futureMethod(List<Id> accId){
       // Logic
    }
}

Future Method to update Accounts

public class AccountProcessor{
    @future
    public static void countContacts(List<Id> accId){
        List<Account> lstOfAccounts = [Select Id, Number_of_Contacts__c, (Select Id from Contacts) from Account Where Id IN :accId];
        
        List<Account> lstOfAcc = new List<Account>();        
        for(Account acc : lstOfAccounts){
            if(acc != null){
                Integer contCount = 0;
                if(acc.Contacts != null && acc.Contacts.size()> 0)
                    contCount = acc.Contacts.size();
                acc.Number_of_Contacts__c = contCount;
                lstOfAcc.add(acc);
            }
        }
        Update lstOfAcc;
    }
}

4. Queueable apex syntax

You can chain one job to another job by starting a second job from a running job. Chaining jobs is useful if you need to do some sequential processing.

public class SampleQueueable implements Queueable{
   
    public void execute(QueueableContext qc){
        // Logic
    }
}


Create an Queueable Apex class that inserts Contacts for Accounts

public class AddPrimaryContact implements Queueable{
    Contact con;
    String state;
    
    public AddPrimaryContact(Contact con, String state){
        this.con = con;
        this.state = state;
    }
    public void execute(QueueableContext qc){
        List<Account> lstOfAccs = [SELECT Id FROM Account WHERE BillingState = :state LIMIT 200];
        
        List<Contact> lstOfConts = new List<Contact>();
        for(Account acc : lstOfAccs){
            Contact conInst = con.clone(false,false,false,false);
            conInst.AccountId = acc.Id;
        
            lstOfConts.add(conInst);
        }
        
        INSERT lstOfConts;
    }
}


5. Soap inbound web service syntax. 

global class AccountCreationWebServiceUsingSoap {
   
   webservice static String createAccount(String accName) {
        Account acc = new Account();
acc.Name = accName;
insert accName;
return stringValue; // response
    }
}

6. Rest web service syntax. 

@RestResource(urlMapping='/Account/*')
global with sharing class MyRestResource {
 
  @HttpPost
    global static String doPost(String name,
        String phone, String website) {
        Account account = new Account();
        account.Name = name;
        insert account;
        return account.Id;
    }
}

7. Rest callouts to send data to external syntax. 

Apex Rest callout - POST:

Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json;charset=UTF-8');
request.setBody('{"name":"mighty moose"}');
HttpResponse response = http.send(request);
if (response.getStatusCode() != 201) {
    System.debug('The status code returned was not expected: ' +
        response.getStatusCode() + ' ' + response.getStatus());
} else {
    System.debug(response.getBody());
}

8. Soap outbound webservice syntax.

public PageReference doSave(){
        partnerSoapSforceCom.Soap sp = new partnerSoapSforceCom.Soap();
        partnerSoapSforceCom.LoginResult log = sp.login('username, 'pwd');
        
        soapSforceComSchemasClassAccountcre.SessionHeader_element SessionHeader = 
        new soapSforceComSchemasClassAccountcre.SessionHeader_element();
        SessionHeader.sessionId = log.sessionId;
        
        soapSforceComSchemasClassAccountcre.AccountCreationWebServiceUsingSoap AccountCreationWebService = 
        new soapSforceComSchemasClassAccountcre.AccountCreationWebServiceUsingSoap();
        AccountCreationWebService.SessionHeader = SessionHeader;
  }


9. AURA Component to display records

Server Side Controller:

public with sharing class AccountCtrl { 
    
    @AuraEnabled
    public static List<Account> getAccountData(){
       return [select Id,Name, AccountNumber from Account  limit 50];
    }      
}

Component

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes"
                access="global"
                controller="AccountCtrl">
    <aura:handler name="init" value="{!this}" action="{!c.doinit}"/>
    <aura:attribute name="Acclist"  type="list"/>
    <div>
        <table class="slds-table slds-table_cell-buffer slds-table_bordered">
            <thead>
                <tr class="slds-line-height_reset">
                    
                    <th class="slds-text-title_caps" scope="col">
                        <div class="slds-truncate" title="Account Name">Account Name</div>
                    </th>            
                    
                </tr>
            </thead>
            <tbody>
                <aura:iteration items="{!v.Acclist}" var="item" >
                    <tr class="slds-hint-parent">
                        <td data-label="Account Name">
                            <div class="slds-truncate" title="{!item.Name}">{!item.Name}</div>
                        </td>
                        
                    </tr>
                </aura:iteration>
            </tbody>
        </table>
    </div>
</aura:component>


Client Side Controller

({
    doinit: function(component) {
        var action = component.get('c.getAccountData');
        var self = this;
        action.setCallback(this, function(actionResult) {
            component.set('v.Acclist', actionResult.getReturnValue());
        });
        $A.enqueueAction(action);
    }    
})

Aura App:

<aura:application extends="force:slds">
<c:DisplayAccounts/>
</aura:application>

10. LWC to display records

Apex Class:

public  class AccountController 
{
   @AuraEnabled(cacheable=true)
   public static List<Account> displayAccounts(){
       return [select Id, Name, Site from Account];
   }
}

DisplayAccounts.html

<template>
<table class="slds-table slds-table_cell-buffer slds-table_bordered">
<tr>
<td><b>Name</b></td>
<td><b>Site</b></td>
<td><b>Action</b></td>
</tr>
<template for:each={accounts.data} for:item="acc">
<tr key={acc.Id}>
<td>
{acc.Name}
</td>
<td>
{acc.Site}
</td>
</tr>
</template>
</table>
</template>
</template>

DisplayAccounts.js

import { LightningElement,api,wire } from 'lwc';
import displayAccounts from '@salesforce/apex/AccountController.displayAccounts';
import updateRecord from '@salesforce/apex/AccountController.updateRecord';
import { refreshApex } from '@salesforce/apex';

export default class UpdateAccount extends LightningElement {
@api currentRecordId;
@api errorMessage;
    @wire(displayAccounts) accounts;    
}

LightningApplication

<aura:application extends="force:slds">
<c:DisplayAccounts/>
</aura:application>


11. Trigger to fetch values from Custom Metadata type and update in ISO__c field of Account:

Trigger updateIsoCode on Contact(before insert) {
  List < Country_ISO_Code__mdt > countryIsoCodeMetadata = [SELECT Id,
  Country__c, ISO_Code__c FROM Country_ISO_Code__mdt
  ];
  Map < String, String > countryIsoCodeMap = new Map < String, String > ();
  if (countryIsoCodeMetadata != null && countryIsoCodeMetadata.size() > 0) {
   for (Country_ISO_Code__mdt cmd: countryIsoCodeMetadata) {
    countryIsoCodeMap.put(cmd.Country__c, cmd.ISO_Code__c);
   }
  }
  for (Contact con: Trigger.new) {
   if (countryIsoCodeMap.containsKey(con.Country__c)) {
    System.debug('inside Contact loop @@@ ' + countryIsoCodeMap);
    con.ISO_Code__c = countryIsoCodeMap.get(con.Country__c);
   }
  }
 }

12. Trigger to create Opportunity line item when Opportunity is created.

  trigger Create_Opportunity_Line_Item_19 on Opportunity(after insert) {
   Pricebook2 standardPb = [select id, name, isActive from Pricebook2 where IsStandard = true limit 1];
   Product2 prd1 = new Product2(); // ----> Create  product
   prd1.Name = 'Accomodation';
   prd1.isActive = true;
   insert prd1;
   System.debug(prd1);

   PricebookEntry pbe1 = new PricebookEntry(); //------->Create PriceBookEntry
   pbe1.Product2ID = prd1.id;
   pbe1.Pricebook2ID = standardPb.id;
   pbe1.UnitPrice = 50;
   pbe1.isActive = true;
   insert pbe1;
   List < OpportunityLineItem > oplist = new List < OpportunityLineItem > (); //-->Create List to store OpportunityLineItem    
   for (Opportunity opp: Trigger.New) {
    OpportunityLineItem oppli = new OpportunityLineItem(); //---->Create OpportunityLineItem.
    oppli.PricebookEntryId = pbe1.Id;
    oppli.OpportunityId = opp.Id;
    oppli.Quantity = 5;
    oppli.TotalPrice = 10.0;
    oplist.add(oppli);
   }
   insert oplist; //----->insert OpportunityLineItem
  }

13. Trigger to count Contacts of an Account incase of lookup relationship.

 Trigger ContactCountTrigger on Contact(After insert, After Delete, After Undelete, After Update) {
  Set < Id > setAccountIds = new Set < Id > ();
  if (Trigger.isInsert || Trigger.isUndelete || Trigger.IsUpdate) {
   for (Contact con: Trigger.new) {
    setAccountIds.add(con.AccountId);
   }
  }
  if (Trigger.isDelete) {
   for (Contact con: Trigger.old) {
    setAccountIds.add(con.AccountId);
   }
  }
  List < Account > listAccs = [Select id, name, number_of_contacts__c,
   (Select id from contacts) from Account
   where Id in: setAccountIds
  ];
  for (Account acc: listAccs) {
   acc.number_of_contacts__c = acc.contacts.size();
  }
  update listAccs;
 }

14. Trigger to delete/undelete child records when parent records are deleted/undeleted. 

 trigger DeleteContacts on Account(after delete) {
  List < Contact > contacts = [SELECT AccountId FROM Contact
   WHERE AccountId IN: Trigger.OldMap.keyset()
  ];
  if (contacts != null && contacts.size() > 0) {
   delete contacts;
  }
 }

15. Trigger to undelete contacts when Account is undeleted.

 trigger undeleteContacts on Account(after undelete) {
  List < Contact > contacts = [SELECT AccountId FROM Contact
   WHERE AccountId IN: Trigger.OldMap.keyset()
   AND IsDeleted = true ALL ROWS
  ];
  if (contacts != null && contacts.size() > 0) {
   undelete contacts;
  }
 }

16. Test class:

@isTest(seeAllData=false)
private class AccountTriggerWithHelperClass_Test{
    static testMethod void testHelperClass(){
        List<Account> listAccs = TestDataUtility.createAccounts();
        insert listAccs;
    }
}




17. Test Data utility class:

public class TestDataUtility{
    public static List<Account> createAccounts(){
        List<Account> accts = new List<Account>();        
        for(Integer i=0;i<20;i++) {
            Account a = new Account(Name= ConstantsUtility.ACCOUNTNAME + i);
            accts.add(a);
        }  
        return accts;
    }
}

18. Constants Utility class:

public class ConstantsUtility {
    public static String ACCOUNTNAME = 'Test Account';
    public static String ACCOUNTOBJ = 'Account';
    public static String CONTACTOBJ = 'Contact';
}

19. Apex XML parsing:

String strResp = '<?xml version="1.0" encoding="UTF-8"?><breakfast_menu>  <food>    <name>Belgian Waffles</name>    <price>$5.95</price>    <description>Two of our famous Belgian Waffles with plenty of real maple syrup</description>    <calories>650</calories>  </food>  <food>    <name>Strawberry Belgian Waffles</name>    <price>$7.95</price>    <description>Light Belgian waffles covered with strawberries and whipped cream</description>    <calories>900</calories>  </food>  <food>    <name>Berry-Berry Belgian Waffles</name>    <price>$8.95</price>    <description>Light Belgian waffles covered with an assortment of fresh berries and whipped cream</description>    <calories>900</calories>  </food>  <food>    <name>French Toast</name>    <price>$4.50</price>    <description>Thick slices made from our homemade sourdough bread</description>    <calories>600</calories>  </food>  <food>    <name>Homestyle Breakfast</name>    <price>$6.95</price>    <description>Two eggs, bacon or sausage, toast, and our ever-popular hash browns</description>    <calories>950</calories>  </food></breakfast_menu>';  

Dom.Document doc = new Dom.Document();  
doc.load( strResp );  
Dom.XMLNode rootElement = doc.getRootElement();  
for ( Dom.XMLNode childElement : rootElement.getChildElements() ) {  
      
    for ( Dom.XMLNode detailElement : childElement.getChildElements() )  
        system.debug( detailElement.getName() + '-' + detailElement.getText() );  
      
 
Output:

name-Belgian Waffles
price-$5.95
-
-

20. Database.SaveResult: 


List<Account> listAccount = new List<Account>();
Account acc = new Account();
acc.Name = 'testAccount1';

Account acc2 = new Account();
acc2.Name = 'testAccount2';

listAccount.add(acc);
listAccount.add(acc2);

System.debug('listAccount '+listAccount);

if(listAccount != null && listAccount.size()>0){
    Database.SaveResult[] srList = Database.insert(listAccount, false);
    System.debug('srList ' +srList);
    for(Database.SaveResult sr: srList){
        if(sr.isSuccess()){
            System.debug('Records inserted successfully '+sr.getId());
        }
        else
        {
            for(Database.Error objErr: sr.getErrors()){
                System.debug('The following error has occurred ');
                System.debug(objErr.getStatusCode() + ': '+objErr.getMessage());
                System.debug('Error occurred by fields '+objErr.getFields());
            }
        }
    }
}

21. Trigger to count, sum, min, max, avg of child records using Aggregate Result:


Trigger ContactAggregateResult on Contact(after insert){
    
    Set<Id> accountIdSet = new Set<Id>();
    Map<Id, Account> accountMap = new Map<Id, Account>();
    for(Contact con: Trigger.new){
        if(con.AccountId != null){
            accountIdSet.add(con.AccountId);
        }
    }
    if(accountIdSet != null){
        for(AggregateResult ar: [SELECT count(Id) contactCount, sum(Amount__c) sumAmount, 
                                 avg(Amount__c) avgAmount, Max(Amount__c) maxAmount,
                                 min(Amount__c) minAmount, AccountId FROM Contact 
                                 WHERE AccountId in: accountIdSet GROUP BY AccountId ])
        {
            Account acc = new Account();
            acc.Id = (Id)ar.get('AccountId');
            acc.Count_of_Contacts__c = (Integer)ar.get('contactCount');
            acc.Sum_of_Contacts_Amount__c = (Decimal)ar.get('sumAmount');
            acc.Max_of_Contacts_Amount__c = (Decimal)ar.get('maxAmount');
            acc.Min_of_Contacts_Amount__c = (Decimal)ar.get('minAmount');
            acc.Average_Of_Contacts_Amount__c = (Decimal)ar.get('avgAmount');
            System.debug('Count @@@ '+acc.Count_of_Contacts__c);
            System.debug('Sum @@@ '+acc.Sum_of_Contacts_Amount__c);
            System.debug('Max @@@ '+acc.Max_of_Contacts_Amount__c);
            System.debug('Min @@@ '+acc.Min_of_Contacts_Amount__c);
            System.debug('Avg @@@ '+acc.Average_Of_Contacts_Amount__c);                         
            accountMap.put(acc.Id, acc);
        }
    }
    if(accountMap != null){
        update accountMap.values();
    }
}


22. Delete multiple object records using batch apex.

global class SampleBatchClass implements Database.Batchable<string>, Database.Stateful, Schedulable  {
    
    global boolean reRun = false; 
    global Set<string> setToAddresses = new Set<String>();
    global Set<string> setCcAddresses = new Set<String>();
    
    global List<string> setToAddressesList = new List<String>();
    global List<string> setCcAddressesList = new List<String>();
    
    global SampleBatchClass(){
        for(CEP_Storage_Email__mdt mdt: [SELECT Label, Set_To_Addresses__c, Set_Cc_Addresses__c FROM CEP_Storage_Email__mdt 
                                         WHERE Active__c = TRUE LIMIT 50000 ])
        {
            if(mdt.Set_To_Addresses__c){
                setToAddresses.add(mdt.Label);
            }
            if(mdt.Set_Cc_Addresses__c){
                setCcAddresses.add(mdt.Label);
            }
        }
        setToAddressesList = new List<String>(setToAddresses);
        setCcAddressesList = new List<String>(setCcAddresses);
    }
    
    global Iterable<string> start(Database.BatchableContext ctx) {
        Set<String> setMetadata = new Set<String>();
        for(CEP_ObjectName__mdt mdt: [SELECT Label, Days__c FROM CEP_ObjectName__mdt WHERE Active__c = FALSE  
                                     /* AND Days__c != NULL AND Days__c = 90 */  LIMIT 50000])
        {
            //setMetadata.add(mdt.Label);
            setMetadata.add(mdt.Label+'-'+mdt.Days__c); //
        }
        List<String> listMetadata = new List<String>(setMetadata);
        return listMetadata;
    }
    
    global void execute(Database.BatchableContext ctx, list<string> lstsObjectName) {
        list<sObject> objectRecords = new list<sObject>();
        List<String> valueArray = new List<String>(); //
        for(string strObjectName : lstsObjectName) {
            valueArray = strObjectName.split('-'); //
            
            System.debug('valueArray[0] $$$ '+valueArray[0]);
                System.debug('valueArray[1] $$$ '+valueArray[1]);
            
            Integer numV = Integer.valueOf(valueArray[1]);
            for(sObject sObj : Database.query('SELECT Id FROM ' + valueArray[0] + ' WHERE CreatedDate < Last_N_days:' + Integer.valueOf(valueArray[1]) + ' limit 150')) {
                System.debug('sObj $$$ '+sObj);
                if(objectRecords.size() < 200*(500 - objectRecords.size()) + objectRecords.size()){ // Here 500 is batch size.
                    objectRecords.add(sObj);
                }
                else {
                    reRun = true;
                    break;
                } 
            }
        }
        if(objectRecords != NULL && objectRecords.size()>0){
            objectRecords.sort();
            try{
                database.delete(objectRecords, false);
            }
            catch(Exception ex){
                System.debug('Exception occurred '+ex);
            }
        } 
    }
    
    global void finish(Database.BatchableContext ctx) {
        if(reRun) {
            Database.executebatch(new SampleBatchClass());
        }
        if(!reRun) {
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            
            AsyncApexJob a = [SELECT a.TotalJobItems, a.Status, a.NumberOfErrors,
                              a.JobType, a.JobItemsProcessed, a.CompletedDate, a.ApexClassId  
                              FROM AsyncApexJob a WHERE id = :ctx.getJobId()];
            
            String sandboxUrl1=String.valueOf(System.Url.getOrgDomainUrl());
            String sandboxUrl2=sandboxUrl1.replace('Url:[delegate=','');
            String sandboxUrlValue = sandboxUrl2.replace(']','');
            
            mail.setToAddresses(setToAddressesList);
            mail.setCcAddresses(setCcAddressesList);
           // mail.setReplyTo(Label.CEP_Set_Reply_To_Email_Address);
            mail.setSenderDisplayName('Scheduled Batch Apex Processing');
            mail.setSubject('Batch Apex Processing status for 7 days old records: '+a.Status);
            String htmlBody = '';
            //open table..
            htmlBody ='Hi, <br/> <br/> As part of Platform Environment Management, the scheduled batch apex job has processed & 
            cleared all the test data which are created 7 days ago for specific objects.<br/><br/>';
            htmlBody +='Please find below details of scheduled batch apex job.<br/><br/>';
            htmlBody += '<table border="1" style="border-collapse: collapse">';
            htmlBody += '<tr><td>' + '  &nbsp; Apex Job Processing status  &nbsp; ' + '</td><td>' + ' &nbsp;'+ a.Status + ' &nbsp;' + '</td></tr>';
            htmlBody += '<tr><td>' + '  &nbsp; Total Batch Apex Job Items  &nbsp;' + '</td><td>' + ' &nbsp;' +  a.TotalJobItems + ' &nbsp;' +  '</td></tr>';
            htmlBody += '<tr><td>' + '  &nbsp; Job Item processed  &nbsp;' + '</td><td>' + ' &nbsp;' + a.JobItemsProcessed + ' &nbsp;' + '</td></tr>';
            htmlBody += '<tr><td>' + '  &nbsp; Number Of Failures   &nbsp;' + '</td><td>' + ' &nbsp;' +  a.NumberOfErrors + ' &nbsp;' + '</td></tr>';
            htmlBody += '<tr><td>' + '  &nbsp; Job Completed Date and Time  &nbsp;' + '</td><td>' + ' &nbsp;' +  a.CompletedDate + ' &nbsp;' + '</td></tr>';
            htmlBody += '<tr><td>' + '  &nbsp; Job Type  &nbsp;' + '</td><td>' + ' &nbsp;' + a.JobType + ' &nbsp;' + '</td></tr>';
            htmlBody += '<tr><td>' + '  &nbsp; Apex Class Id  &nbsp;' + '</td><td>' + ' &nbsp;' + a.ApexClassId  + ' &nbsp;' + '</td></tr>';
            htmlBody += '<tr><td>' + '  &nbsp; Scheduled Apex Class Name  &nbsp;' + '</td><td>' + ' &nbsp;' + 'CEP_BatchDelete7DaysOldRecords'  + ' &nbsp;' + '</td></tr>';
            htmlBody += '<tr><td>' + '  &nbsp; Sandbox URL  &nbsp;' + '</td><td>' + ' &nbsp;' + sandboxUrlValue + ' &nbsp;' + '</td></tr>'; 
            htmlBody += '</table>'; //close table
            htmlBody +='<br/><br/>Thank you.<br/>';
            
            mail.setHTMLBody(htmlBody);
            Messaging.sendEmail(new Messaging.Singleemailmessage [] {mail});
        }  
    } 
    
    global void execute(SchedulableContext SC) {
        Database.executebatch(new SampleBatchClass());
    }
}