Tuesday 18 September 2018

Salesforce: Get the latest date record from object list against each Map Key

There is a scenario in which we have a territory object and against each territory, we have one to many sales rep. So there is a child object named SalesRep_To_Terr__c. This child object contains an effective date field and isActive flag. For any territory, there might be more than 1 active record in this object but I was needed to get the latest among all the active record. Latest means which has the most recent effective date. So to get that latest effective date first I maintained a Map contains the Sales Rep Id as key and list of SalesRep_To_Terr__c.

Initially, I have maintained a set of all territory

map<String , list<SalesRep_to_Terr__c>> MapUserSRT = new map<String , list<SalesRep_to_Terr__c>>();
Set<Id> sTerr = new Set<Id>();   // contains all the Territory
List<datetime> LstDT ;
datetime maxTDate ;

for(SalesRep_to_Terr__c terr :  [select Effective_Date__c,Sales_Representative__c ,Active__c  from SalesRep_to_Terr__c where Effective_Date__c != null  and Territory__c IN:  sTerr and active__c= true order by Effective_Date__c LIMIT 1])
{
      if(MapUserSRT.containsKey(terr.Sales_Representative__c))
      {
            list<SalesRep_to_Terr__c> lstDWP = MapUserSRT.get(terr.Sales_Representative__c);
            lstDWP.add(terr);
            MapUserSRT.put(terr.Sales_Representative__c, lstDWP);
              
      }
      else
      {
            MapUserSRT.put(terr.Sales_Representative__c, new List<SalesRep_to_Terr__c> { terr   });
       }
}

Now the sole of this topic to get the latest among each territory is given below

for(String UserRec : MapUserSRT.keyset())
{
            LstDT = new List<datetime>();
            for(SalesRep_to_Terr__c Terr : MapUserSRT.get(UserRec))
            {
                LstDT.add(Terr.Effective_Date__c);
            }
           
            LstDT.sort();
            maxTDate = LstDT.get(LstDT.size()-1);
           
            for(SalesRep_to_Terr__c Terr : MapUserSRT.get(UserRec))
            {
                if(Terr.Effective_Date__c == maxTDate)
                {
                    if(Terr.Active__c)
                    {
                        MapUserLTerritory.put(Terr.Territory__c,UserRec);
                        break;
                    }
                }
                    
            }
  }

The yellow part above actually describes to first fill the list of datetime for each user and then sort it. The default sort function sort the list to Ascending order means the recent date would be at the end of the list on the last index. MaxTDate variable holds the recent date. Then against each Sales rep Territory record check the date and Active flag. Lastly maintained a Map contains the Territory as Key and Sales rep as the value


Salesforce.com Event Overlap handling through trigger

Workflow or Process Builder is not capable of handling the overlapping of OOB event timing, therefore, it requires a trigger. The scenario was to show an error message to the user on creating an event that this start time and end date overlaps with another event of the same owner and the user should get the link of that previously saved event. The trigger should be written on Before Insert and Before Update to prevent it from saving the event record. I also asked to only check the overlap event on some particular record types. Therefore first filled a SET of those record types and match that event has the same record types or not.

The Trigger code is given below



Trigger EventValidation on Event(before insert, before update)
{
    
   EventUtility Utility = new EventUtility();
   set<id> RtIds = new set<id>();
  
    for(RecordType rt : [SELECT Id FROM RecordType WHERE DeveloperName in ('Test_RecordType1',' Test_RecordType2')])
        RtIds.add(rt.id);
   
    system.debug('RtIds :'+RtIds);
    List<Event> selectedRtEvent = new list<Event>();
    for(Event t : trigger.new)
    {
        system.debug('t.recordtype.name' + t.recordtypeid);
       
        if(RtIds.contains(t.recordtypeid))
            selectedRtEvent.add(t);
    }
    system.debug('selectedRtEvent ::' + selectedRtEvent );
    if(selectedRtEvent.size() > 0)
    {
        system.debug('selectedRtEvent : ' + selectedRtEvent);
        Utility.ValidateRecord(selectedRtEvent);
    }
 }


The bottom yellow highlighted lines call the utility class which validates the overlapping of event timings.

Code of Utility Class given below

public with sharing class EventUtility
{   
   
    public void EventUtility()
    {}
   
    public void ValidateRecord(List<Event> LstEvent)
    {
        map<Id,List<Event>> mEvent = new map<Id,List<Event>>();
        set<Id> sOwner = new set<Id>();
        for(Event t : LstEvent)
        {
            sOwner.add(t.ownerid);
        }
       
        for(Event t: [SELECT OwnerId, WhoId, WhatId, StartDateTime, EndDateTime FROM Event where OwnerId in :sOwner])
        {
            if(mEvent.containsKey(t.OwnerId))
            {
                list<Event> lst = mEvent.get(t.OwnerId);
                lst.add(t);
                mEvent.put(t.OwnerId,lst);
            }
            else
            {
                list<Event> lst = new list<Event>();
                lst.add(t);
                mEvent.put(t.OwnerId,lst);
            }
        }
       
       
        String errorMsg = 'Record Already Exists : ';
        String baseUrl = URL.getSalesforceBaseUrl().toExternalForm();
        boolean IsOverlap = false;
        for(Event newRecords : LstEvent)
        {
           
           
            if(mEvent.containsKey(newRecords.ownerid))
            {
                for(Event userrecords : mEvent.get(newRecords.ownerid))
                {
                   
                    IsOverlap = false;
                    IsOverlap = TimePeriodOverlap(newRecords.StartDateTime,newRecords.EndDateTime,userrecords.StartDateTime,userrecords.EndDateTime);
                    system.debug('IsOverlap: ' + IsOverlap);
                   
                    if (IsOverlap == true && (userrecords.ownerid == newRecords.ownerid))
                    {
                        newRecords.addError(errorMsg + '<a target="_blank" href =' +baseUrl+'/'+userrecords.id+'> (Duplicate Record)</a>',false);
                    }
                   
                }
            }
        }
       
       
    }      
        public boolean TimePeriodOverlap(DateTime BS, DateTime BE, DateTime TS, DateTime TE)
        {
            // More simple?
            // return !((TS < BS && TE < BS) || (TS > BE && TE > BE));

            // The version below, without comments
            /*
            return (
                (TS >= BS && TS < BE) || (TE <= BE && TE > BS) || (TS <= BS && TE >= BE)
            );
            */

            return (
                // 1. Case:
                //
                //       TS-------TE
                //    BS------BE
                //
                // TS is after BS but before BE
                (TS >= BS && TS < BE)
                || // or

                // 2. Case
                //
                //    TS-------TE
                //        BS---------BE
                //
                // TE is before BE but after BS
                (TE <= BE && TE > BS)
                || // or

                // 3. Case
                //
                //  TS----------TE
                //     BS----BE
                //
                // TS is before BS and TE is after BE
                (TS <= BS && TE >= BE)
            );
        }
       
   
}