Tuesday, August 6, 2013

Custom Sort feature at Salesforce - Part II


Read post: Custom Sort feature at Salesforce - Part I

In Salesforce we often face cases where we need to customize Sort feature. Sort means sorting of records in related list of any parent record. For example: Opportunity has its child records of Opportunity Line Items. In related list of OLIs at an Opportunity detail page, standard 'Sort' button is present. Clicking on this sort button navigates to a Salesforce standard page where we can sort based on only Product names.


But we can not override Sort button with custom Visualforce page for our customized features. So I took a workaround where I can sort records based on say 3 fields of OLI while click sort button .
First, create 2 custom fields at Opportunity.Sorted_By_field__c and Sorted_By_Asc__c

1- Add a section from page layout of Opportunity, then add a VF page at that section 
which shows custom Related list of OLI. And there is button 'Sort Oli'.
2- Click on custom button 'Sort OLI' at that related list. Go to your page.
N.B. You may add custom Sort button at standard related list of OLI also 
which will lead to your page for custom sort. But when you sort there and come back then 
sorted order will not be shown in standard related list.
3- Sort then. click Save Sort there.
4- back to Opportunity detail page.


1- Add a VF page at a section from Edit Layout of Opportunity, where my custom button ' Sort OLI' will be present.





VF page 'NewSectionPageFOrOLI':
<apex:page standardController="Opportunity" extensions="customOLIController">
<apex:form >
    <apex:pageblock id="pageBlock" >
        <apex:pageblockButtons >
        <apex:commandButton value="Sort These" action="{!sortthese}" dir=""/>
        </apex:pageblockButtons>

        <apex:pageBlockTable id="pbt" value="{!sortedList}" var="wOn" >
          
            <!-- Displaying the Field Values -->
            <apex:column value="{!wOn.Quantity}" />
            <apex:column value="{!wOn.TotalPrice}"/>
          
        </apex:pageBlockTable>
    </apex:pageblock>
</apex:form>
</apex:page>


And the controller of this page where code for button lies is as follow:

public class customOLIController {
   

    private String sortedBy = null;
    private Boolean sortAscending = null;
    private AP_SortHelperCopied sorter = new AP_SortHelperCopied ();
    public List<OpportunityLineItem> sortedList {get;set;}
    public Opportunity opp;
    
    public customOLIController (ApexPages.StandardController controller) {
        this.opp = (Opportunity)controller.getRecord();
        Opportunity currOpp = [SELECT Id,Sorted_By_field__c, Sorted_By_Asc__c FROM Opportunity WHERE Id = :opp.Id];
        sorter.originalList = [SELECT Quantity, TotalPrice FROM OpportunityLineItem WHERE OpportunityId =:opp.id];
        if(currOpp.Sorted_By_field__c != null &&  currOpp.Sorted_By_field__c != '')
            sortedList = (List<OpportunityLineItem>)sorter.getSortedList(currOpp.Sorted_By_field__c, currOpp.Sorted_By_Asc__c, opp.id);
     
    }
    public PageReference sortthese(){
    
    return new PageReference ('/apex/DemoSortII?Id='+opp.Id);
    }
    
     public customOLIController() {
    }
    
    public PageReference Save() {
        PageReference pr = new PageReference('/apex/NewSectionPageFOrOLI') ;
        pr.setRedirect(false);
        sorter.saveOpp(opp.id);
        return pr;
    }
    public PageReference SortByQuantity() {
        setSortedBy('Quantity');

        sortedList = (List<OpportunityLineItem>) sorter.getSortedList('Quantity', sortAscending, opp.id);
        return null;
    }
    public PageReference SortByTotalPrice() {
        setSortedBy('TotalPrice');

        sortedList = (List<OpportunityLineItem>) sorter.getSortedList('TotalPrice', sortAscending,opp.id);
        return null;
    }
    
    public List<OpportunityLineItem> getList() {
        if (sortedList == null) {
            SortByQuantity();
        }
        return sortedList;
    }
    private void setSortedBy(String value) {
        if (sortedBy == value) {
             sortAscending = !sortAscending;
        } else {
            sortAscending = true;
        }
        sortedBy = value;
    }
}
The class called from this class is:

public class AP_SortHelperCopied {     // 
    public Map listPosition = null;    // >
    public Map> sortedFieldValuesPerFieldName = null;     // >>
    public Map>> sObjectIDsPerFieldNames = null;
    public String OpportuniId;
    public Boolean ascIs;
    public String fieldIs;
// Properties
    public List originalList {get; set;}
    
// Constructor
    public AP_SortHelperCopied () {
        originalList = null;
    }
    // Public Method
    public List getSortedList(String fieldName, Boolean ascending, String oppId) {
        if (originalList == null) {
            // Assume that originalList has a not NULL value.
            // If the class who uses this method has not assigned a value it will get an Exception which
            //    needs to be handled by the calling class.            // Force the exception...
            originalList.clear();
        }        // Make field name uppercase
        fieldName = fieldName.toUpperCase();        // Get sorted list
        OpportuniId = oppId;
        return makeSortedList(fieldName, ascending);
    }
    public List getSortedList(List originalList, String fieldName, Boolean ascending, String oppId) {
        this.originalList = originalList;
        sortedFieldValuesPerFieldName = null;
        OpportuniId = oppId;
        return getSortedList(fieldName, ascending,OpportuniId);
    }
    
// Private Methods
    public void InitializeFieldName(String fieldName) {
        String sObjectID;
        Integer position;
        String fieldValue;
        List sObjectIDs = null;
        Set valuesForFieldSet = null;    // Sets automatically omit duplicate values 
        List valuesForFieldList = null;
        Map> sObjectIDsPerFieldValues = null;
        
        // Make sortedFieldValuesPerFieldName
        if (sortedFieldValuesPerFieldName == null) {
            listPosition = new Map();
            sortedFieldValuesPerFieldName = new Map>();
            sObjectIDsPerFieldNames = new Map>>();
        }
        
        // Get (or create) map of sObjectIDsPerFieldValues
        sObjectIDsPerFieldValues = sObjectIDsPerFieldNames.get(fieldName);
        if (sObjectIDsPerFieldValues == null) {
            sObjectIDsPerFieldValues = new Map>();
            sObjectIDsPerFieldNames.put(fieldName, sObjectIDsPerFieldValues);
        }
        if (!sortedFieldValuesPerFieldName.keySet().contains(fieldName)) {
            // Objects need to be initialized
            position = 0;
            valuesForFieldSet = new Set();
            listPosition = new Map();
            
            for (sObject sObj : originalList) {
                sObjectID = sObj.ID;
                fieldValue = getValue(sObj, fieldName);
                
                // Add position to list
                listPosition.put(sObjectID, position++);
                
                // Add the value to the set (sets rather than lists to prevent duplicates)
                valuesForFieldSet.add(fieldValue);
                
                // Get (or create) map of sObjectIDs
                sObjectIDs = sObjectIDsPerFieldValues.get(fieldValue);
                if (sObjectIDs == null) {
                    sObjectIDs = new List();
                    sObjectIDsPerFieldValues.put(fieldValue, sObjectIDs);
                }
                
                // Add ID to sObjectIDs
                sObjectIDs.add(sObjectID);
            }
            
            // Sort set items (Need to convert to list)
            valuesForFieldList = new List();
            valuesForFieldList.addAll(valuesForFieldSet);
            valuesForFieldList.sort();
            
            // Now add it to the map.
            sortedFieldValuesPerFieldName.put(fieldName, valuesForFieldList);
        }
    }
    public List makeSortedList(String fieldName, Boolean ascending) {
        Integer position;
        List sObjectIDs = null;
        List valuesForFieldList = null;        // Initialize objects
        InitializeFieldName(fieldName);        // Get a list of the same type as the "originalList"
        List outputList = originalList.clone();
        outputList.clear();        // Get a list of sorted values
        valuesForFieldList = sortedFieldValuesPerFieldName.get(fieldName);
        
        // for each sorted value
        for (String fieldValue : valuesForFieldList) {
            // Get lisft of IDs
            sObjectIDs = sObjectIDsPerFieldNames.get(fieldName).get(fieldValue);
            
            // for each ID
            for (String ID : sObjectIDs) {
                // Get position in originalList
                position = listPosition.get(ID);                // Add each sObject to the list.
                if ((ascending) || (outputList.size()==0)) {
                    outputList.add(originalList[position]);
                } else {
                    outputList.add(0, originalList[position]);
                }
            }
        }
        System.debug('\n\nI am returning the output = '+fieldName+'and = \n'+ascending+'\n');
       
        fieldIs = fieldName;
        ascIs = ascending;
        

        return outputList;
    }
    public void saveOpp(String OpportuniIdp){
        Opportunity currOpp = [SELECT Id FROM Opportunity WHERE Id =:OpportuniIdp];
        currOpp.Sorted_By_field__c = fieldIs;
        currOpp.Sorted_By_Asc__c = ascIs;
        
        upsert currOpp;
        
    
    }
    public String getValue(sObject sObj, String fieldName) {
        // This returns the sObject desired in case the fieldName refers to a linked object.
        Integer pieceCount;
        String[] fieldNamePieces;
        
        fieldNamePieces = fieldName.split('\\.');
        pieceCount = fieldNamePieces.size();
        for (Integer i = 0; i < (pieceCount-1); i++) {
            sObj = sObj.getSObject(fieldNamePieces[i]);
        }
        return String.valueOf(sObj.get(fieldNamePieces[pieceCount-1]));
    }
 
    
}

2- Notice that Sort button click will navigate to DemoSortII, a VF page where customization for sorting based on 2/3 columns present. In below image, you can see table at DemoSortII page where OLIs are there and each column has a button which gives facility to sort by that field.Finally clicking on save returns to original Opportunity detail page and related list shows sorted list by our choice which we have made at DemoSortII page.

VF page DemoSortII:
<apex:page standardController="Opportunity" extensions="customOLIController" >
  <apex:form >
    <apex:pageBlock >
    <apex:commandButton action="{!Save}"  value="Save" />
      <apex:pageBlockSection columns="1" ID="AjaxTable">
        <apex:datatable value="{!List}" var="acc" Border="1" cellspacing="1" cellpadding="5">
          <apex:column >
            <apex:facet name="header">
              <apex:commandButton action="{!SortByQuantity}" value="Sort By Quantity" rerender="AjaxTable" />
            </apex:facet>
            <apex:outputText value="{!acc.Quantity}" />
            </apex:column>
          <apex:column >
            <apex:facet name="header">
            <apex:commandButton action="{!SortByTotalPrice}" value="Sort By Price" rerender="AjaxTable" />
            </apex:facet>
            <apex:outputText value="{!acc.TotalPrice}" />
          </apex:column>
        </apex:datatable>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
</apex:page>


Controller of this class is same as above class customOLIController.


No comments:

Post a Comment