Wednesday, November 23, 2016

Best practices and desing patterns for declarative development

As we all know that salesforce provides a rich set of declarative development features such as validation rules, workflows, process builders etc. These declarative features are optimized to work with the force.com architecture and provides us with some default degree of SOC. Having said that as an admin / developer there are few best practices or patterns that you can introduce in your declarative development. 

Make your validation rules, process builders and workflows mutable by design

Having Validation Rules, Workflow and Process builders in your Salesforce org is a great way to implement your business process. However, I can think of more than one scenario where ValidationWorkflow Rules and Process builders can get in your way. To avoid such scenarios always ensure that your declarative components are mutable by design
.

Step 1: Create a hierarchy custom setting


Step 2: Create custom field (checkbox) for each validation rules, If your organization has more than 300 validation rules, you should create 1 custom setting per object.


Step 3: Update validation rules to By Pass based on the value in custom setting


Step 4: When you want to bypass a validation rule, create a record in the custom setting. You can create record for a user or profile or at org level.





Step 5: The same logic can be applied to workflow rules and process builders.

Filter based on record type

If you have record types enabled for objects, always ensure that your validation rules, workflows, process builders and approval process are filters on record type name. Even if the process is global and applicable to all record types its recommended to apply record type filters. This will ensure that any future development or expansion in your org will not impact your existing codes.

Sample: Add RecordType.DeveloperName check after your user/profile bypass check




Add more focus to your declarative components

Ensure that the entry criteria is narrowed down to the relevant events. For eg: Validation rules are executed every-time a record is created or modified, its recommended to narrow down the scope to change in values of the relevant fields.

Sample: Add isNew() isChanged(Field Name) check after record type check.







Tuesday, November 8, 2016

Lightning Component Framework - Approval History Lightning Component


What is Lightning Component Framework?
Simply put Lightning Components is a UI framework for developing dynamic and responsive web apps for mobile and desktop device. Like any other app framework Lightning Components is also collection of codes and services at both client side and server side.




What consists of Lightning Components?
Lightning component bundle mainly consists of the below resources



Is Lightning going to replace Visualforce?
As far as we know, Visualforce is not going away (Atleast not in the near future). Visualforce provides the mechanism for delivering template-driven web pages and email messages. In addition, developers wishing to simply utilize a basic container and maintain more control over the lifecycle of the request may still choose visualforce.

For more detailed comparison click here 

Lets see some example 
Use Case: Record page component to display approval history 

Apex Class: ApprovalComponentController

 public class ApprovalComponentController {  
       /*  
       * This method will be called by the helper function  
       * Parameter: recordId  
       * Returns: instance of wrapper class  
       */  
   @AuraEnabled  
   public static ApprovalList getApprovalData(Id recId)  
   {  
     Id recordId = recId;  
     ApprovalList approvalResultForObject = new ApprovalList();  
     List<ApprovalStepWrapper> aSW = new List<ApprovalStepWrapper>();  
     String recallApprovalProcessLink;  
     Boolean isSubmitForApproval = true;  
     for(ProcessInstance pI: getProcessHistory(recordId).values())  
     {  
       Map<Id,List<ProcessInstanceHistory>> mapOfProcessNodeIdAndProcessInstanceHistory = new Map<Id,List<ProcessInstanceHistory>>();  
       Set<Id> processNodeId= new Set<Id>();  
       for(ProcessInstanceHistory sWI:pI.StepsAndWorkitems)  
       {  
         if(processNodeId.size() ==0)  
           processNodeId.add(sWI.ProcessNodeId);  
         else if(processNodeId.size()>0 && processNodeId.contains(sWI.ProcessNodeId)!= NULL)  
           processNodeId.add(sWI.ProcessNodeId);  
       }  
       for(Id pNId: processNodeId)  
       {  
         ApprovalStepWrapper aSWr = new ApprovalStepWrapper();  
         for(ProcessInstanceHistory sWI:pI.StepsAndWorkitems)  
         {  
           if(sWI.processNodeId == pNID)  
           {  
             aSWr.listOfSteps.add(new ApprovalHistoryWrap(sWI.CreatedDate, sWI.OriginalActor.Name, sWI.StepStatus,sWI.Actor.Name));  
           }  
           if(sWI.StepStatus == 'Pending')  
           {  
             isSubmitForApproval = false;  
           }  
         }  
         aSW.add(aSWr);  
       }  
     }  
     approvalResultForObject.approvals = aSW;  
     approvalResultForObject.recordId = recordId;  
     approvalResultForObject.isSubmitForApproval = isSubmitForApproval;  
     system.debug('asw'+aSW);  
     return approvalResultForObject;  
   }  
   /*  
    * This method queries the processinstance and workitem for the record  
    * Parameter: Record ID   
    * Returns: Map of all processinstance related to the record id  
    */  
   @AuraEnabled  
   public static Map<Id,ProcessInstance> getProcessHistory(Id objectId)  
   {  
     return new Map<Id,ProcessInstance>([SELECT Id, (SELECT ID, ProcessNodeId,  
                             StepStatus,Comments,TargetObjectId,ActorId,CreatedById,IsDeleted,IsPending  
                             ,OriginalActorId,ProcessInstanceId,RemindersSent,CreatedDate, Actor.Name,  
                             OriginalActor.Name , ProcessNode.Name FROM StepsAndWorkitems order by CreatedDate DESC )   
                       FROM ProcessInstance where TargetObjectId =:objectId order by CreatedDate DESC]);  
   }  
   /*  
    * Wrapper class  
    */  
   public class ApprovalStepWrapper{  
     @AuraEnabled  
     public List<ApprovalHistoryWrap> listOfSteps {get;set;}  
     public ApprovalStepWrapper(){  
       listOfSteps = new List<ApprovalHistoryWrap>();  
     }  
   }  
        /*  
    * Wrapper class  
    */  
   public class ApprovalHistoryWrap  
   {  
     @AuraEnabled  
     public Date createdDate {get;set;}  
     @AuraEnabled  
     public string actorName {get;set;}  
     @AuraEnabled  
     public string steps {get;set;}  
     @AuraEnabled  
     public string assignedTo {get;set;}  
     public ApprovalHistoryWrap(DateTime crDate, string name, string stp, string actor)  
     {  
       createdDate = crDate.date();  
       actorName = name;  
       steps = stp;  
       assignedTo = actor;  
     }  
   }  
   /*  
    * Wrapper class  
    */  
   public class ApprovalList  
   {   
     @AuraEnabled  
     public List<ApprovalStepWrapper> approvals {get;set;}  
     @AuraEnabled    
     public Boolean isSubmitForApproval {get;set;}  
     @AuraEnabled  
     public Id recordId {get;set;}  
     public ApprovalList(){  
       approvals = new List<ApprovalStepWrapper>();  
       isSubmitForApproval = true;  
     }  
   }  
 }  


Highlights:
@AuraEnabled: The @AuraEnabled annotation enables client- and server-side access to an Apex controller method. Using this annotation makes your method visible to your lightning components.

Access wrapper class in lightning component: To access properties of your Wrapper class in your lightning component using @AuraEnabled annotation.


Lightning component: ApprovalListComp

 <aura:component controller="ApprovalComponentController" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global" >  
      <aura:attribute name="recordId" type="Id"/>  
   <aura:handler name="init" value="{!this}" action="{!c.doInit}" />  
   <aura:attribute name="approvalList" type="object"/>  
   <ltng:require styles="{!$Resource.SLDS+'/assets/styles/salesforce-lightning-design-system-ltng.css'}" />  
   <!-- WRAPPER DIV -->  
   <div class="wk_static">  
     <!-- BODY -->  
     <div class="slds-scrollable" style="height: 300px;">  
       <table class="slds-table slds-table--bordered slds-max-medium-table--stacked">  
         <thead>  
           <tr class="slds-text-title--caps">  
             <th scope="col" >  
               <div class="slds-truncate" title="Date">Date</div>  
             </th>  
             <th scope="col" >  
               <div class="slds-truncate" title="Status">Status</div>  
             </th>  
             <th scope="col" >  
               <div class="slds-truncate" title="Assigned To">Assigned To</div>  
             </th>  
             <th scope="col" >  
               <div class="slds-truncate" title="Approver">Approver</div>  
             </th>  
           </tr>  
         </thead>  
         <tbody>  
           <!-- aura equivalent of apex:repeat -->  
           <aura:iteration items="{!v.approvalList.approvals}" var="appRec">   
             <aura:iteration items="{!appRec.listOfSteps}" var="step">  
               <tr >  
                 <td data-label="Date">  
                   <div class="slds-truncate" title="Date">{!step.createdDate}</div>  
                 </td>  
                 <td data-label="Status">  
                   <div class="slds-truncate" title="Status">{!step.steps}</div>  
                 </td>  
                 <td data-label="Assigned To">  
                   <div class="slds-truncate" title="Assigned To">{!step.assignedTo}</div>  
                 </td>  
                 <td data-label="Approver">  
                   <div class="slds-truncate" title="Approver">{!step.actorName}</div>  
                 </td>  
               </tr>  
             </aura:iteration>  
           </aura:iteration>   
         </tbody>  
       </table>  
     </div>  
   </div>  
 </aura:component>  

Highlights:
force:hasRecordId: The interface indicates the current record ID should be injected in the component's recordId attribute

<aura:iteration>: <aura:iteration> iterates over a collection of items and renders the body of the tag for each item. Data changes in the collection are re-rendered automatically on the page. aura:iteration supports iterations containing components that have server-side dependencies or that can be created exclusively on the client-side

JS Controller

 ({  
      doInit : function(component, event, helper) {  
           helper.getApprovalList(component);  
      }  
 })  

Highlights
helper.getApprovalList:  Calls helper function from a client-side controller functionHelper functions are local to a component, improve code reuse, and move the heavy lifting of JavaScript logic away from the client-side controller where possible.


JS Helper

 ({  
      getApprovalList : function(component) {  
     var action = component.get("c.getApprovalData");  
     action.setParams({  
       recId: component.get("v.recordId")  
     });  
     action.setCallback(this, function(a) {  
         component.set("v.approvalList", a.getReturnValue());  
       });  
     $A.enqueueAction(action);  
      }  
 })