Friday, November 08, 2019

Loading Status in Visualforce Page without Apex Class

The loading status is the spinning ... the in progress ...the glass hour thingy or whatever you want to call it as long the purpose is to prevent user to perform any action before the previous action is completed.


  • use action:status
  • no controller class required
  • if you are using with  apex:command tag, put rerender if not the loading status will not work

 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
  <style>
                .spinnerBg{
                    width: 100%;
                    height: 100%;
                    position: absolute;
                    background-color: #000;
                    opacity: 0.2;
                    z-index: 999999;
                }
                .spinner{
                    width: 100%;
                    height: 100%;
                    position: absolute;
                    background-image: url("/img/loading32.gif");
                    background-size: 16px;
                    background-repeat: no-repeat;
                    background-attachment: fixed;
                    background-position: center;
                    z-index: 9999999;
                    opacity: 1;
                }
   </style>
   
    <apex:actionStatus id="spinnerStatus">    
                    <apex:facet name="start">
                     <div class="spinnerBg" />
                    <div class="spinner" />
                </apex:facet>
    </apex:actionStatus>
   
     <apex:commandButton action="{!save}" value="Save" status="spinnerStatus" reRender="theForm"/>

Friday, October 04, 2019

Checking Salesforce Classic or Salesforce LEX using javascript

Simple as this :

1
2
3
4
5
6
7
8
9
var isClassic ='false';

 console.log ('@@@ UIThemeDisplayed '+ '
{!$User.UIThemeDisplayed}');
 if('{!$User.UIThemeDisplayed}
' == 'Theme3')
{
   isClassic='true'; 
}

To use visualforce page within Salesforce LEX is to use this url . Some scenario that you might want to apply this is when you want to redirect to Visualforce page in Lightning.

1
/one/one.app#/alohaRedirect/apex/apexpagename?parameter

Friday, August 23, 2019

Method to retrieve correct ID from formula field.

One idea that really helpful to be delivered. Please vote
Reduce the compiled formula character cost of CASESAFEID()

Correct way to store ID in formula

Formula field only return 15 char ID and will be treated as String / Text . Which mean it will be treated as case insensitive in SOQL.

SELECT Id,Name,Parent.Id, Parent.Name, Ultimate_Parent_Account_ID1__c FROM Account WHERE Ultimate_Parent_Account_ID1__c ='001xxxxxxxxxQpK'

Will return record regardless the letter case
And also another  risk if  we query this with 18 digit ID as it will return 0 data.

As conclusion, normal formula with text return type is not able to capture the Salesforce Id correctly. 
In order to make it return as ID use CASESAFEID() . However this formula consumes so much character so that is the reason why you need to vote above idea.

Retrieve the correct Id from formula in Apex

If we are not able to use casesafeid in formula field , then we might need to be careful when retrieve record using formula field because SOQL is case-insensitve and ID is case sensitive. Honestly I wish that Salesforce provides a function that I can use directly in SOQL something like this ‘where casesafeid(Ultimate_Parent_Account_ID1__c) = ='001xxxxxxxxxQpKAAU' but unfortunately it is not available for this time being.

In order to ensure the Id is correct , we need to cast it to Id .Please note that I cast String to ID :
 Id ultimateid = (ID)account.Ultimate_Parent_Account_ID1__c . 

With this I can use the actual Id for further processing.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
String accountId15Char = '001xxxxxxxxxQpK';//because it will not entertain Id type or 18 character
Map&lt;Id,Account> mapUltimateAccount =new Map&lt;Id,Account>
                                      ([Select Id ,Parent.Id,Parent.Name  ,Ultimate_Parent_Account_ID1__c from Account where
                Ultimate_Parent_Account_ID1__c=:accountId15Char]);
        
         system.debug('@@@ mapUltimateAccount ' + mapUltimateAccount.size());
        //ultimate id contains complete hierarchy 
        //and exclude the account itself should be more than 1
        if(mapUltimateAccount.size()>1){
            for(Id accountId :mapUltimateAccount.keySet()){
               Account account =mapUltimateAccount.get(accountId);
               Id ultimateid = (ID)account.Ultimate_Parent_Account_ID1__c ;
               system.debug('Original Ultimate Id ' + account.Ultimate_Parent_Account_ID1__c+ ' , after convert to Id '+ ultimateid);
      
      //here you can have mechanism to compare id whether it returns the correct id
  
            }
           
        }

I think that's it for today. See you in next entry.

Monday, August 05, 2019

Soap UI fail to handshake with Salesforce

I faced this when doing Salesforce Trailhead : SOAP API Unit , it has nothing to do with token or password but something to do with TLS version. The solution can be referred here but I will re-write back how I solved this.

Note that I am using Windows 10 and SoapUI 5.3.0 .

Navigate to your SoapUI folder where it should be something C:\Program Files (x86)\SmartBear\SoapUI-5.3.0\bin . Notice there .vmoptions file.


Open that file and paste this

-Dsoapui.https.protocols=SSLv3,TLSv1.2
So it end result will look like this .
-XX:MinHeapFreeRatio=20

-XX:MaxHeapFreeRatio=40

-Xms128m

-Xmx1000m

-Dsoapui.properties=soapui.properties

-Dsoapui.https.protocols=SSLv3,TLSv1.2

-Dsoapui.home=C:\Program Files (x86)\SmartBear\SoapUI-5.3.0/bin

-Dsoapui.ext.libraries=C:\Program Files (x86)\SmartBear\SoapUI-5.3.0/bin/ext

-Dsoapui.ext.listeners=C:\Program Files (x86)\SmartBear\SoapUI-5.3.0/bin/listeners

-Dsoapui.ext.actions=C:\Program Files (x86)\SmartBear\SoapUI-5.3.0/bin/actions

-Dwsi.dir=C:\Program Files (x86)\SmartBear\SoapUI-5.3.0/wsi-test-tools

-Djava.library.path=C:\Program Files (x86)\SmartBear\SoapUI-5.3.0/bin

-Djava.util.Arrays.useLegacyMergeSort=true

-splash:SoapUI-Spashscreen.png
It should working now. Happy developing!

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();
  
  }

Monday, June 24, 2019

Tips and Tricks : Make apex:column value display conditionally

When try to make apex:column display conditionally there high possibility we try to code like below which will prompt Syntax error. Missing ')' .Unfortunately this is documented in any where so far.



<apex:column headerValue="Actual Approver" value="{!IF(item.objectType=='Qualification',item.bid.Actual_Final_Approver__c,item.quote.Actual_Final_Approver__c)}"></apex:column>

apex:column cannot being rendered directly in value tag but we can render it different way like below
    <apex:column headerValue="Actual Approver">
                    <apex:outputField rendered="{!item.objectType=='Qualification'}"
                                      value="{!item.bid.Actual_Final_Approver__c}"/>
                    <apex:outputField rendered="{!item.objectType=='Quote'}"
                                      value="{!item.quote.Actual_Final_Approver__c}"/>
                </apex:column>
The render is done not in value tag in column but use render tag in component like outputText, outputField inside apex:column. Note that using outputField for lookup field will generate standard mini page layout when we mouse over on the link.

Reference : apex:column

Monday, February 25, 2019

Tips and Tricks : Avoid $$ in ANT password

Today we faced weird issue. ANT throwing Invalid User Password although we know exactly what our password.We able to login directly into Salesforce site but we failed to login via ANT.

It delays our deployment process because we struggle to figure out why.

  • Double check the password [Done]
  • Reset the password[Done]
  • Check proxy[Done]


Thankfully I found this tips : Password Issue in ANT . I suspect it may due to this

Property Expansion
When Ant encounters a construct ${some-text} the exact parsing semantics are subject to the configured property helper delegates.
$$ Expansion
In its default configuration Ant will expand the text $$ to a single $ and suppress the normal property expansion mechanism for the text immediately following it, i.e., $${key} expands to ${key} and not value even though a property named key was defined and had the value value. This can be used to escape literal $ characters and is useful in constructs that only look like property expansions or when you want to provide diagnostic output like in Read More...

Anyone has better explanation please comment.For this time being, it's better avoid $$ or more than two $ in password.

Wednesday, January 02, 2019

Summer 18 : Better way to retrieve RecordTypeId

Salesforce Summer ‘18 has added new method to get Developer Name for Record Types which give me such a huge relieve.

my sketch
Previously, we were only able to get RecordType using Id or Name. Usually in our code we always need to retrieve the Id but retrieving Id with Name (Label) using getRecordTypeInfosByName is not good idea as Name can be changed easily.

To solve the issue, we created one Global class to run SOQL just to retrieve ID, DeveloperName. It exposed our code to SOQL limit and we need to be careful with the design when we decided to use Static because the global class can be called by different method in the same transaction.

Now we can forget about that *tedious part*. I started to clean up my code to use Salesforce method instead of using SOQL to retrieve record type.

Caution : Update your class API version to the latest version to prevent error as this new method not recognized in old version.

Retrieve RecordType ID using DeveloperName


Id newRecordType = Schema.SObjectType.Opportunity.getRecordTypeInfosById().get(opportunityId).getDeveloperName();

Retrieve RecordType DeveloperName using ID


String recordType= Schema.SObjectType.Opportunity.getRecordTypeInfosByDeveloperName().get(recordTypeDeveloperName).getRecordTypeId();

Please update the underlined word above with correct value.