Thursday, 27 February 2020

customize omni channel logic to distribute cases based on Case Creation Date

Omni Channel queues distributes cases, based on Date/Time the case is assigned to the queue.
we can customize this logic to look for some other attribute such as Case Creation Date.

Scenario : A new case is created and assigned to the Queue “A”. There were already 10cases in the queue. The newly cases when assigned to the queue is 11th in the queue. After the first 10 cases are distributed, the 11th case is pushed to the agent.
to over come from waiting time in Queue We can the distribute cases  based on Case Creation Date.

Source : https://developer.salesforce.com/docs/atlas.en-us.omni_channel_dev.meta/omni_channel_dev/sforce_api_objects_pendingservicerouting.htm
solution :

Object: PendingServiceRouting
field name : CustomRequestedDatetime
field  Description:  Retains the datetime of a work item’s initial request, so work items are rerouted using the datetime of the initial work request. When left blank, work items are rerouted using the datetime when they were rerouted.

in case trigger onAfterUpdate
we need to write future method or Queueableclass.


public static void ReprioritizeBasedonCreateddate(){
          List<PendingServiceRouting> lstPendingServiceRoutingtoupdate = new List<PendingServiceRouting>();
         Map<Id, PendingServiceRouting> routingByRoutingId = new Map<Id, PendingServiceRouting>();
         Set<id> CaseIds= new Set<id>();
         for(PendingServiceRouting objPendingService : [Select Id,QueueId, CustomRequestedDatetime, IsReadyforRouting, RoutingPriority, WorkItemId from PendingServiceRouting
                                                        where Queueid IN:'queue ids as per requirement'                                             
                                                        ]) {
             
             if(objPendingService.WorkItemid!=null){
                     if(objPendingService.WorkItemid.getSObjectType().getDescribe().getName()=='Case'){
                                    CaseIds.add(objPendingService.WorkItemid);
                                    routingByRoutingId.put(objPendingService.id, objPendingService);
                     } 
             }
       }
             Map<ID,Case> CaseMap=new  Map<ID,Case> ([select id,CreatedDate from Case where id in:CaseIds]);
             for(PendingServiceRouting objPendingService : routingByRoutingId.values()){
                        
                 if(CaseMap.containsKey(objPendingService.WorkItemid) && CaseMap.get(objPendingService.WorkItemid)!=null){
                     objPendingService.CustomRequestedDatetime  =CaseMap.get(objPendingService.WorkItemid).CreatedDate ;
                     
                     lstPendingServiceRoutingtoupdate.add(objPendingService);   
                 }
             }
               if(lstPendingServiceRoutingtoupdate.size()>0 && !lstPendingServiceRoutingtoupdate.isEmpty()){
                           List<Database.SaveResult> srs = Database.update(lstPendingServiceRoutingtoupdate, false);

              }

     }

Friday, 17 November 2017

Task Custom Record Type Selection Lightning Component with Related(whatID) pre populate (force:createRecord event)

To Make Custom Record Type Selection Available for Users in Custom Components With force:createRecord i Have created below custom Lightning component . by using this when user create task from any object like Opportunity  it will navigate to Task Record Type Selection page , once he select recordtype it will open editable mode Task record with pre populated Relatedto field (opportunity).

Lightning component:
<aura:component controller="recordtypeController" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global">
    
    <aura:handler name="init" value="{!this}" action="{!c.fetchListOfRecordTypes}"/>
    
    <aura:attribute name="lstOfRecordType" type="String[]" />
    <aura:attribute name="isOpen" type="boolean" default="false" />
    <aura:attribute name="recordId" type="String" />
  <div class="slds-m-around--x-large">
    <lightning:button label="Create a Task" onclick="{!c.openModal}" />
  </div>    
   <!-- Model Box Start -->    
    <aura:if isTrue="{!v.isOpen}">
        <div role="dialog" tabindex="-1" aria-labelledby="header43" class="slds-modal slds-fade-in-open">
            <div class="slds-modal__container">
                <div class="slds-modal__header">
                    <button class="slds-button slds-modal__close slds-button--icon-inverse" title="Close" onclick="{!c.closeModal}">
                        X<span class="slds-assistive-text">Cancel</span>
                    </button>
                    <h2 id="header43" class="slds-text-heading--medium">New Task</h2>
                </div>
                
                <div class="slds-modal__content slds-p-around--medium">
                    <div class="slds-grid slds-wrap">
                        <div class="slds-size--1-of-2 slds-large-size--1-of-2">
                             <div class="slds-align--absolute-center">Select a Record Type</div>                            
                        </div>
                        <div class="slds-size--1-of-2 slds-large-size--1-of-2">
                            <ui:inputSelect aura:id="selectid">
                                <aura:iteration items="{!v.lstOfRecordType}" var="Objtask">                            
                                    <ui:inputSelectOption text="{!Objtask}" label="{!Objtask}"  />
                                </aura:iteration>
                            </ui:inputSelect>
                        </div>&nbsp; &nbsp;
                    </div>                   
                </div>
                
                <div class="slds-modal__footer">
                    <lightning:button class="slds-button slds-button--neutral" onclick="{!c.closeModal}">Cancel</lightning:button>
                    <lightning:button class="slds-button slds-button--brand" onclick="{!c.createRecord}">Next</lightning:button>
                </div>
            </div>
        </div>
        <div class="slds-backdrop slds-backdrop--open"></div>
    </aura:if>
</aura:component>


Javascript controller:
({
   /* On the component Load this function call the apex class method, 
    * which is return the list of RecordTypes of object 
    * and set it to the lstOfRecordType attribute to display record Type values
    * on ui:inputSelect component. */
   fetchListOfRecordTypes: function(component, event, helper) {
      var action = component.get("c.fetchRecordTypeValues");
      action.setCallback(this, function(response) {
         component.set("v.lstOfRecordType", response.getReturnValue());
      });
      $A.enqueueAction(action);
   },
   /* In this "createRecord" function, first we have call apex class method 
    * and pass the selected RecordType values[label] and this "getRecTypeId"
    * apex method return the selected recordType ID.
    * When RecordType ID comes, we have call  "e.force:createRecord"
    * event and pass object API Name and 
    * set the record type ID in recordTypeId parameter. and fire this event
    * if response state is not equal = "SUCCESS" then display message on various situations.
    */
   createRecord: function(component, event, helper) {
      component.set("v.isOpen", true);
      var action = component.get("c.getRecTypeId");
      var recordTypeLabel = component.find("selectid").get("v.value");
      action.setParams({
         "recordTypeLabel": recordTypeLabel
      });
      action.setCallback(this, function(response) {
         var state = response.getState();
         if (state === "SUCCESS") {
            var createRecordEvent = $A.get("e.force:createRecord");
            var RecTypeID  = response.getReturnValue();
             var whatidvar  = component.get("v.recordId");
             alert(whatidvar);
            createRecordEvent.setParams({
               "entityApiName": 'Task',
               "recordTypeId": RecTypeID,
                 'defaultFieldValues': {
                'WhatId': whatidvar
          }

            });
            createRecordEvent.fire();
             
         } else if (state == "INCOMPLETE") {
            var toastEvent = $A.get("e.force:showToast");
            toastEvent.setParams({
               "title": "Oops!",
               "message": "No Internet Connection"
            });
            toastEvent.fire();
             
         } else if (state == "ERROR") {
            var toastEvent = $A.get("e.force:showToast");
            toastEvent.setParams({
               "title": "Error!",
               "message": "Please contact your administrator"
            });
            toastEvent.fire();
         }
      });
      $A.enqueueAction(action);
   },
   closeModal: function(component, event, helper) {
      // set "isOpen" attribute to false for hide/close model box 
      component.set("v.isOpen", false);
   },
   openModal: function(component, event, helper) {
      // set "isOpen" attribute to true to show model box
      component.set("v.isOpen", true);
   },
})


Server side Controller :
public class recordtypeController {
    public static Map<Id, String> recordtypemap {get;set;}
    
   @AuraEnabled        
    public static List<String> fetchRecordTypeValues(){
        List<Schema.RecordTypeInfo> recordtypes = Task.SObjectType.getDescribe().getRecordTypeInfos();    
        recordtypemap = new Map<Id, String>();
        for(RecordTypeInfo rt : recordtypes){
            if(rt.getName() != 'Master')
            recordtypemap.put(rt.getRecordTypeId(), rt.getName());
        }        
        return recordtypemap.values();
    }
    
    @AuraEnabled
    public static Id getRecTypeId(String recordTypeLabel){
        Id recid = Schema.SObjectType.Task.getRecordTypeInfosByName().get(recordTypeLabel).getRecordTypeId();        
        return recid;
    }      
}

Test class:
@isTest

public class recordtypeControllerTest {


static testmethod void testFetchRecordTypes() {
    List<String> values = recordtypeController.fetchRecordTypeValues();
  }

 static testmethod void testgetRecordTypeId() {
    String recordTypeLabel = 'Test1';
    ID testId = recordtypeController.getRecTypeId(recordTypeLabel);
    System.assert(testId != null);
  }

}

Tuesday, 23 May 2017

Custom approval process : to Add Custom fields to "Items to Approve" home page component

I  have approval process on Opportunity. my requirement is like to display custom field (TC Meeting Notes)to "Items to Approve" home page component, we can't customise this, but we can create a custom component for this behaviour using Visualforce.  i have created vf page apex class and
custom home page component .
here TC_Meeting_Notes__c is custom field from opportunity :
Idea link: https://success.salesforce.com/ideaView?id=08730000000BrKHAA0



VF page:
<apex:page controller="ItemstoApprovvecontroller" sidebar="false" showHeader="false" tabStyle="Approver_Configuration__c" >
    <apex:form >
        <apex:pageBlock title="Items To Approve">
            <apex:pageBlockTable value="{!items_to_approve}" var="item_to_approve">
                <apex:column headerValue="Action" width="160 px" >
                                    <apex:commandLink target="_top" value="Reassign |" action="{!REASSIGNnavigation}" style="text-decoration:none;color: #015ba7;" styleClass="cactionLink">
                                                       <apex:param name="myParam" value="{!item_to_approve.approvalid }" />
                                                                                             
                                                                    </apex:commandLink>

                    <apex:commandLink target="_top" value=" Approve / Reject" action="{!ApproveRejectnavigation}" style="text-decoration:none;color: #015ba7;"  >

                   <apex:param name="myParam" value="{!item_to_approve.approvalid }" />
                </apex:commandLink>

         
                </apex:column>
               <apex:column headerValue="Type"  width="100 px">
                    <apex:outputText >{!item_to_approve.objtype}
                      </apex:outputText>
                </apex:column>
             
                <apex:column headerValue="Deal" width="300 px">
                    <apex:outputLink target="_top" value="/{!item_to_approve.id}">{!item_to_approve.name}
                      </apex:outputLink>
                </apex:column>
             
             
                <apex:column headerValue="TC Meeting Notes">
                    <apex:outputtext >{!item_to_approve.tcmeetingcomments}
                      </apex:outputtext>
                </apex:column>
                <apex:column headerValue="Date Submitted" width="150 px">
                    <apex:outputtext >{!item_to_approve.DateSubmited }
                      </apex:outputtext>
                </apex:column>
            </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>
</apex:page>

Apex class:
public class ItemstoApprovvecontroller {
 
    ApexPages.standardController stdController= null;
    public ItemstoApprovvecontroller(ApexPages.StandardController controller) {
        stdController=controller;
    }
    public opportunity Objectopportunity {get; set;}
    public List<opportunity> lstopportunityapprove {get; set;}
    ID Oppyid;
    Set<ID> oppyids=new Set<ID>();
 
    public class item_wrapper {
        public item_wrapper(id id,string name,string objtype,String DateSubmited,string tcmeetingcomments, id approvalid ) {
            this.id = id;
            this.name = name;
            this.objtype = objtype;
            this.DateSubmited = DateSubmited;
            this.tcmeetingcomments=tcmeetingcomments;
            this.approvalid =approvalid ;
        }
        public id id { get; set; }
        public string name { get; set; }
        public string objtype { get; set; }
        public String DateSubmited { get; set; }
        public string tcmeetingcomments{ get; set; }
        public id approvalid { get; set; }
     
    }
 
    public list<item_wrapper> items_to_approve { get; set; }
 
    public ItemstoApprovvecontroller() {
        items_to_approve = new list<item_wrapper>();
     
        map<id,ProcessInstanceWorkItem> mpaPIWIdToPIW = new map<id,ProcessInstanceWorkItem>();
        list<ProcessInstanceWorkItem> lstPIWI = [select processinstance.targetobjectid,CreatedDate ,processinstance.targetobject.name,ProcessInstance.TargetObject.type from processinstanceworkitem where actorid = :userinfo.getuserid() Order by CreatedDate Desc];
        if(!lstPIWI.isEmpty()){
            for(ProcessInstanceWorkItem item: lstPIWI) {
                oppyids.add(item.processinstance.targetobjectid);
                if(!mpaPIWIdToPIW.containsKey(item.processinstance.targetobjectid)){
                    mpaPIWIdToPIW.put(item.processinstance.targetobjectid,item);
                }
            }
        }
     
        map<id,Opportunity> mapoptyIdtoMeetingnotes = new map<id,Opportunity>();
     
        if(oppyids.size()>0){
            lstopportunityapprove=[select id,Owner.name,name,Deal_Comments__c,stagename,TC_Meeting_Notes__c  from Opportunity where id in : oppyids];
            if(!lstopportunityapprove.isEmpty()){
                for(opportunity objoppy:lstopportunityapprove){
                    mapoptyIdtoMeetingnotes.put(objoppy.id,objoppy);
                }
            }
        }
        if(!lstPIWI.isEmpty()){
         
            for(ProcessInstanceWorkItem item: mpaPIWIdToPIW.values()) {
                String dateTimeValue = item.CreatedDate.format('MM/dd/yyyy hh:mm a');
                system.debug(dateTimeValue +'Debug2 dateTimeValue ');
                if(item.processinstance.TargetObject.type == 'Opportunity'){
                                    system.debug(item.processinstance.targetobjectid +'Debug2 dateTimeValue ');

                    items_to_approve.add(new item_wrapper(item.processinstance.targetobjectid,item.processinstance.targetobject.name,item.processinstance.TargetObject.type,dateTimeValue ,mapoptyIdtoMeetingnotes.get(item.processinstance.targetobjectid).TC_Meeting_Notes__c,item.id ));
                }else{
                   system.debug(item.processinstance.targetobjectid +'Debug2 dateTimeValue ');


                    String sObjName = item.processinstance.targetobjectid.getSObjectType().getDescribe().getLabel();
                                                           system.debug(sObjName +'sObjNameValue ');

                    items_to_approve.add(new item_wrapper(item.processinstance.targetobjectid,item.processinstance.targetobject.name,sObjName ,dateTimeValue ,'',item.id ));
                }
            }
        }
    }  
    public  static String ApproveRejectnavigation() {
        String url='';
        string myParam = apexpages.currentpage().getparameters().get('myParam');
        url='https://'+ System.URL.getSalesforceBaseUrl().getHost() +
            '/p/process/ProcessInstanceWorkitemWizardStageManager?id=' + myParam ;    
     
        return url;
     
    }
    public  static String REASSIGNnavigation() {
        String url='';
        string myParam = apexpages.currentpage().getparameters().get('myParam');
        url='https://'+ System.URL.getSalesforceBaseUrl().getHost()+'/'+ myParam +'/e?et=REASSIGN';    
        return url;
     
    }
 
}

Sunday, 23 April 2017

Opportunity Owner should get email notification when task is completed on opportunity

trigger NotifyOpportunityowner on Task ( after update) {
List<Task> taskList = new List<Task>();
Set<ID> taskIDSet = new Set<ID>();

if (trigger.old != null)
{
for (Task t: trigger.old)
{
taskIDSet.add(t.ID);
}Set<String> strwhatIDs = new Set<String>();
set<Id> setClosedTaskId = new set<Id>();
Map<id,string> maprecortypes=new Map<id,string>();
Map<id,string> mapEmailAdress=new Map<id,string>();
Map<id,string> mapcurrentOwnerid=new Map<id,string>();
string[] toaddress = New String[] {};
for (Task t : (List<Task>)Trigger.new) {
Task oldTask = (Task)trigger.oldMap.get(t.Id);
Task newTask = (Task)trigger.newMap.get(t.Id);
if(((oldTask.status != newTask.status) && t.status == 'Completed' )){
strwhatIDs.add(t.whatID);
setClosedTaskId.add(t.id);

}  
}
list<Task> lstTask = [select id,OwnerId,what.recordtype.developername,what.recordtype.name from Task where Id IN : setClosedTaskId ];

EmailTemplate objEt =  [select id,developername from EmailTemplate where developername ='Testing_vf_template'];
list<Opportunity> lstopt = [select id,owner.email from Opportunity where id IN: strwhatIDs];

if(!lstopt.isEmpty()){
for(Opportunity Opt : lstopt){

mapEmailAdress.put(Opt.id,Opt.owner.email);

}
}
 
if(!mapEmailAdress.values().isEmpty()){
for(string str :mapEmailAdress.values()){
toaddress = New String[] {str};
}
}
List<Messaging.SingleEmailMessage> lstEmailId=new List<Messaging.SingleEmailMessage>();
Contact cont = [select id,name from contact limit 1];

if(!lstTask.isEmpty()){
for(Task tk : lstTask){
system.debug('toaddress exception'+toaddress);

Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setToAddresses(toaddress);
mail.setTemplateId(objEt.id);
mail.setWhatId(tk.id);
mail.setTargetObjectId(cont.id);
mail.setTreatTargetObjectAsRecipient(false);
mail.setSaveAsActivity(false);
lstEmailId.add(mail);                

}
}
if(lstEmailId.size()>0){
try{              
Messaging.sendEmail(lstEmailId);
}Catch(Exception ee){
system.debug('Print exception'+ee);
}
}
}
 

}

Sunday, 18 December 2016

Custom roll up summary on lookup _ count number of attachments attached on the tasks.

 Count number of attachments attached on the tasks.

 the field (NumberOfAttachments__c)updates when you edit the attachment and save the attachment. Also works if deleting the attachment updates the field. 

trigger CountAttachment on Attachment (after insert, after update, after delete, after undelete) {
    // Contains the IDs of all the parent tasks
    Set<Id> parentTaskIdSet = new Set<id>();

    if (trigger.new != null)
    {
        for (Attachment a: trigger.new)
        {
            parentTaskIdSet.add(a.parentId);
        }
    }
    
    if (trigger.old != null)
    {
        for (Attachment a: trigger.old)
        {
            parentTaskIdSet.add(a.parentId);
        }
    }    
    
    // List of tasks that needs to be updated
    List<Task> parentTaskList = [SELECT id, (SELECT id FROM Attachments) FROM Task WHERE id in: parentTaskIdSet];
    
    for (Task t: parentTaskList)
    {
        t.NumberOfAttachments__c = t.Attachments.size();
    }
    
    update parentTaskList;
}
-----------------------------------------------------------------------------
when you create attachments on a task, you need to edit the task, create the attachments, then save the task. When you are saving the task, we are probably writing over the trigger's update on task. So to fix this we will actually need to write another trigger, this time on Task. And since we will be updating task in the task trigger, we need a class to prevent recursion. Here's what the trigger on Task would look like:

-------------------------------------------------------------------------------
trigger UpdateAttachmentCount on Task (after insert, after update) {
    
    if (checkRecursive.runOnce())
    {
        List<Task> taskList = new List<Task>();
        Set<ID> taskIDSet = new Set<ID>();
        
        if (trigger.old != null)
        {
            for (Task t: trigger.old)
            {
                taskIDSet.add(t.ID);
            }
        }
        
        if (trigger.new != null)
        {
            for (Task t: trigger.new)
            {
                taskIDSet.add(t.ID);
            }
        }
        
        // Query for the attachment children of the tasks
        taskList = [SELECT id, (SELECT id FROM attachments) FROM Task WHERE ID in: taskIDSet];
    
        for (Task t: taskList)
        {
            t.NumberOfAttachments__c = t.Attachments.size();
        }
        
        update taskList;
    }
-----------------------------------------------------------------------------

the class that prevents recursion looks like:
------------------------------------------------------------------------------
public Class checkRecursive{
    private static boolean run = true;
    public static boolean runOnce(){
        if (run){
            run = false;
            return true;
        }
        else
        {
            return run;
        }
    }
}

Tuesday, 18 October 2016

Salesforce Certified Platform App Builder - Summer '16 Release Exam Dumps

App Builder Maintenance:
 S.No
Question
Answer
1
Universal Containers has an app page active for Salesforce1. What two ways can the App Builder activate the app page in Lightning?
>> Use the activation feature in the Lightning App Builder
>> Add the app page’s Lightning Page tab to a custom Lightning Experience navigation menu in Setup.
2
What three chart types are available in Lightning Experience?
>> Funnel
>> Combo
>> Scatter
3
Universal Containers wants to relate a Contact to multiple Accounts. What are the expected two behaviors when the App Builder enables this feature?
>> On Account records, the Related Contact list includes direct Contact and all indirect Contacts.
>> On Contact records, the Related Account list includes primary Account and all indirectly related Accounts.
4
Where are two places Salesforce Knowledge Articles are accessed?
>> Salesforce1
>> Salesforce Classic
5
Which two objects can be customized on a Lighting Experience record page?
>> Lead >> Opportunity


Wednesday, 21 September 2016

example using LIST and MAP | When to use Map Over List | When do we use set, map, list

While working on Developer forum i found lots of question for List ,Set and Map,  like below
1) When we should use Map over the list
2) When do we use set, Map,List
3) Bulkify code with Map
4) Using Maps and Sets in Bulk Triggers
5) Use of Map in Apex class.
6) Map over list to avoid query inside for loop

Solution :-

List, Set, Map are called collections in Apex:
List : A list is an ordered collection
1) so use list when you want to identify list element based on Index Number.
2) list can contain Duplicates

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

Set A set is an unordered collection 
1) Do not contain any duplicate elements. So, use set if you want to make sure that your collection should not contain Duplicates.

EX: Set<Account> accSet = new Set<Account>()

 
Set<String> setString = new Set<String>();
// Add two strings to it
setString .add('item1');
setString .add('item2');

Map : A map is a collection of key-value pairs 
Each unique key maps to a single value. Keys can be any primitive data type, while values can be a primitive, sObject, collection type or an Apex object.

EX: Map<Id, Account> accMap = new Map<Id, Account>();

Map<Integer, String> mapOfString = new Map<Integer, String>();
mapOfString.put(1, 'Amit');               
mapOfString.put(2, 'Rahul');              
System.assert(mapOfString.containsKey(1)); 
String value = mapOfString.get(2); 
System.assertEquals('Rahul', value);
Set<Integer> s = mapOfString.keySet();



Map<StringString> myMap = new Map<StringString>{'a' => 'b''c' => 'd'};






Example 1:-  Using trigger populate the Account Field on the Contact record (only insert scenario) 

If we use List and not Map

Apex Class

trigger ContactTriggerWithList on Contact (before insert) 
{
Set<Id> SetAccountId = new Set<Id>(); // Use set to collect unique account ID

for(Contact con: Trigger.new) 
{
 if(con.AccountId != null) 
 {
  SetAccountId.add(con.AccountId); // add Account in Set
 }
}

if( SetAccountId.size() >0 ) 
{
 List<Account> listAccount = [ Select Id, Name, Type from Account where Id IN :SetAccountId ]; // Query Related Account
 
 for(Contact con: Trigger.new) 
 {
  if(con.AccountId != null)
  {
   for(Account acc: listAccount)
   {
    if(con.AccountId == acc.Id)
    {
     con.Type__c = acc.Type;
    }
   }
  }
 }
} 
}

 
NOTE:- In this we are using List of Accounts, Where we have to loop through the matching Account every time to populate the Type__c in the second for loop. In this case need to use nested loop.

To Above the Nested Loop We can use the Map .

Same Trigger using the Map:


trigger ContactTriggerWithMap on Contact (before insert) 
{
    Set<Id> SetAccountId = new Set<Id>();
    for(Contact cont: Trigger.new) 
 {
        if(cont.AccountId != null) 
  {
            SetAccountId.add(cont.AccountId);
        }
    }
    
    if(SetAccountId.size() > 0 ) 
 {
        Map<Id, Account> mapAccount = new Map<Id, Account>([Select Id, Name, Type from Account where Id IN :SetAccountId ]);
        for(Contact cont: Trigger.new) 
  {
            if(cont.AccountId != null && mapAccount.containsKey(cont.AccountId) ) 
   {
                cont.Type__c = mapAccount.get(cont.AccountId).Type;
            }
        }
    }
}
NOTE:- Here we are using map of Account so we can directly use the Map to get the Account record. No need of 2nd for loop here

customize omni channel logic to distribute cases based on Case Creation Date

Omni Channel queues distributes cases, based on Date/Time the case is assigned to the queue. we can customize this logic to look for some ...