Monday, July 29, 2019

Key Take Away : Sequencing Load Operations

Here some highlight after I have done reading on
Extreme Force.com Data Loading, Part 4: Sequencing Load Operations


Configuring Your Organization for the Data Load

1) To consider enable the parallel recalculation

To enable this feature contact Salesforce Support

2) Create Role Hierarchy
3) Load users, assigning them to appropriate roles
4) Configure Public Read/Write organization-wide sharing defaults on the objects you plan to load. Note : some org may have so much data that changing sharing rules from Public to Private takes very long time, therefore just load the data with sharing model that you will use in Production.

Preparing to Load Data

1) Ensure clean data especially foreign key relationship, note that parallel loads switchs to single execution mode, slowing down the load considerably.
2) Suspend events that fire on insert
3) Perform advance testing to tune batch size for throughput.For both the Bulk API and the SOAP API, look for the largest batch size that is possible without generating network timeouts from large records, or from additional processing on inserts or updates that can’t be deferred until after the load completes.

Executing the Data Load

1) Use fastest operation possible : insert is faster than upsert. insert + update can be faster than upsert
2) When Update only load field that have changed for existing record
3) Group child records by Parent Id , if you use separate batched don't reference the same Parent Id. Why? This can reduce risk of record-locking errors.If this cannot be done, you can choose option of using the Bulk API in serial execution mode to avoid locking from parallel updates.


Configuring Organization for Production

1) Defer sharing calculation before performing some or all of the operations below ; depending on the results of your sandbox.
2) Change Public Read/Write OWD to Public Read Or Private appropriately
3) Create or configure public groups / queue [todo configure to what?]
4) Configure sharing rules
5) If Sharing Calculation is not deferred, create public groups, queues, and sharing rules one at a time, and allow sharing calculations to complete before moving on to the next one.
Note :That this will consume long time when resume sharing calculation for large data.
6) Resume events such insert trigger so validation/data enhancement process (Flow,Workflow etc) can run properly in Production

Monday, July 22, 2019

Tips and Tricks : Approving Approval Process in Test Class

Snippet of test class that replicate how to approve record in Approval Process.d


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
      //Step 1: let say you a test data that you want to be approved      
   MyCustomObject__c bid=[select Id from MyCustomObject__c Where Name=:strName];
  
       //Step 2:  Create an approval request for the bid
      Approval.ProcessSubmitRequest req =     new Approval.ProcessSubmitRequest();
      req.setComments('Submitting request for approval via form');
      req.setObjectId(bid.Id);
     
      //Step 3:  Submit on behalf of a specific submitter
      req.setSubmitterId(UserInfo.getUserId());
   
      //Step 4:  Submit the record to specific process and skip the criteria evaluation
      req.setProcessDefinitionNameOrId('Qualification_Approval');
      req.setSkipEntryCriteria(true);

      //Step 5:  Submit the approval request for the bid
      Approval.ProcessResult result = Approval.process(req);

      //Step 6:  Instantiate the new ProcessWorkitemRequest object and populate it
      Approval.ProcessWorkitemRequest req =
      new Approval.ProcessWorkitemRequest();
      req.setComments('Approving request.');
   req.setAction('Approve');

      //Step 7 : To check the next process after approval being submitted/approved etc.(basically next stage)  
   ProcessInstanceWorkitem pItem = [Select Id from ProcessInstanceWorkitem  where ProcessInstance.TargetObjectId =: bid.id];
      
   // Use the ID from the newly created item to specify the item to be worked
      req.setWorkitemId(pItem.Id);

      //Step 8 : result of process
      Approval.ProcessResult result = Approval.process(req);

Let try to breakout the API one by one

All of above are using Approval API.

Monday, July 15, 2019

Tips and Tricks : How to hide other feature in Chatter Feed in Visualforce page WITHOUT using JQuery ?

Business Requirement

To only enable Post features in Chatter Feed in Visualforce. Other features such as Poll, Upload File etc should be invisible to user  so that user only able to Post in Chatter Feed.

This is just to give idea that you can hide some of standard css element using style css with !IMPORTANT tag if it 's still not update as you want to or you can hide it using javascript during onload.

Example to hide some standard elements using style .


1
2
3
4
5
6
7
8
/**code to hide file upload in chatter comment section **/
.cxcontentcommentactiontext {
display:none;
}

.cxcontentcommentactionimg {
display:none;
}

Example to hide some of element in Chatter Feed using Javascript
1
2
3
4
5
6
7
window.onload = function() {
//this is to hide File upload and any feature except post in Chatter feed.
   const chatterList = document.querySelectorAll('.publisherFeedItemTypeChoices li');
   chatterList[1].style.display='none';
   chatterList[2].style.display='none';
   chatterList[3].style.display='none';
}

Basically if you can see which Element you want to hide. End result should be like below.


Monday, July 01, 2019

Tips and Tricks : Test class for Invocable method


Issue :

I got 100% coverage in my sandbox but when run validation for deployment it returns 0% coverage

It turn out that in my sandbox, I am depending on Process Builder to Invocable Apex class, as long I manipulate test data that fire Process Builder it will call Invocable class. This is not useful when deploying it to Production although it gets deployed together with Process Builder.

The correct way is to direct call Invocable method inside test class itself.

Example of class :


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
global class MyCustomObject_StatusUpdate_Util{
 @InvocableMethod(label='Update Quote Status')
  public static void updateQuote(Request[] requests) {
        Set<Id> setOppId = new Set<Id>();
        List<SBQQ__Quote__c> listQuoteToUpdate = new List<SBQQ__Quote__c>();

        for(Request request : requests) {
           setOppId.add(request.recordId);
        }
       
        if(setOppId.size()>0){
            List<SBQQ__Quote__c> listQuote =[Select Id,BypassValidationRule__c,SBQQ__Status__c from SBQQ__Quote__c where SBQQ__Opportunity2__c IN :setOppId  ];

             for(SBQQ__Quote__c quote : listQuote)  {
                 quote.BypassValidationRule__c = quote.BypassValidationRule__c?false:true;
                 quote.SBQQ__Status__c =requests[0].quoteStatus;
                 listQuoteToUpdate.add(quote);
             } 
            if(listQuoteToUpdate.size()>0){
                 Database.SaveResult [] updateResult = Database.update(listQuoteToUpdate, false);
            }
         }
     }

 global class Request{
        @InvocableVariable(label='Opportunity ID' required=true)
        public Id recordId;

        @InvocableVariable(label='Quote Status' required=true)
        public String quoteStatus;
      
    }
}

Example of test class :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static testMethod void testCallInvocableClass() {
        
     Test.startTest();
     My_Custom_Object__c myObject =[Select Id,Approval_Status__c FROM My_Custom_Object__c WHere Opportunity__c =:quote.SBQQ__Opportunity2__c];

    myObject.Approval_Status__c ='Approved';
    //this will work only in sandbox, not in deployment.Logically this will fire process builder
    update myObject;

 //to makesure test class is cover, need to call invocable class directly, which in this scenario I need to instantiate
 // inner class 
    List<MyCustomObject_StatusUpdate_Util.Request> listRequest =new List<MyCustomObject_StatusUpdate_Util.Request>();
    MyCustomObject_StatusUpdate_Util.Request req = new MyCustomObject_StatusUpdate_Util.Request();
    req.recordId=quote.SBQQ__Opportunity2__c;
    req.quoteStatus='In Progress';
    listRequest.add(req);
    
    //the call the method directly - here my method is updateQuote
    MyCustomObject_StatusUpdate_Util.updateQuote(listRequest);
   
    listQuote =[select id,SBQQ__Status__c  from SBQQ__Quote__c where SBQQ__Opportunity2__c=:listOpp[0].Id ];
    system.assertEquals('In Progress', listQuote[0].SBQQ__Status__c)
  
    Test.stopTest();
  
  }