Showing posts with label Salesforce. Show all posts
Showing posts with label Salesforce. Show all posts

Saturday, May 2, 2015

List of items that cannot be deployed in salesforce

Recently when I was deploying configuration for an org which has not gone live, into UAT I made note of manual tasks as i need to reuse that for deploying from UAT to production. I thought i will add few more items on the list to the list i already had. These items had to be manually configured and cannot be deployed.

  •  Renaming Standard fields labels
  • Deletion of standard Picklist values
  • Configuring Search Layouts for all standard objects
  • Escalation rules and Assignment rules
  • Quote Templates
Always as a golden rule Deploy anything with profiles as all the necessary permissions will be carried over. Please feel free to add anything in the comments

Tuesday, January 22, 2013

Build a dynamic SOQL query string

This post is one of my promises to myself this year to build reusable components. I will try to post a few here. But whenever i write a code, Good design pattern is to make it as reusable as possible.

This Apex method basically returns the dynamic soql query string when you feed the list of field api names, object name , conditions and limit .
[Edit] This works only for soql and does not do aggregate functions or polymorphic soql or sosl

/* Return dynamic soql query string
     Inputs list of field api names
     Object name
     list of Condition strings eg 'name=\'test\'' , 'noofemployees=0'
     limit string eg '50000'*/
     */ ----------------------------------------------------
     usage
     list<string> fieldname = new list<string>();
     fieldname.add('id');
     fieldname.add('name');
     list<string> conditions = new list<string>();
     conditions.add('name=\'test\'');
   
     eg1:dynamicSoqlString(fieldname,'account',null,null)
     eg2:dynamicSoqlString(fieldname,'account',null,'2')  
     eg3:dynamicSoqlString(fieldname,'account',conditions,'2');
     */
     

     public static string dynamicSoqlString(List<string> apiNames,string objectname, list<string> conditions, string limitcount){
        if(objectname == null || apinames.size()==0){
            return null;
        }
        string soqlstring ='select ';
        for(integer counter =0;counter<apinames.size();counter++){
            if(counter!=0){
                soqlstring+=',';
            }
            soqlstring+=apinames[counter];
        }
        soqlstring += ' from '+ objectname;
        if(conditions != null && !conditions.isempty()){
            soqlstring+=' where ';
            for(integer counter =0;counter<conditions.size();counter++){
                if(counter!=0){
                    soqlstring+=' and ';
                }
                soqlstring+=conditions[counter];
            }
        }
        if(limitcount != null && limitcount !=''){
            soqlstring+=' limit '+limitcount;
        }
        return soqlstring;
    }

Saturday, April 7, 2012

Query User access Level in Apex

     With API 24 salesforce has given access to UserRecordAccess table. This gives us more power in querying whether user have access and upto what level so that we can build our VF and apex accordingly.
http://www.salesforce.com/us/developer/docs/api/index_Left.htm#StartTopic=Content/sforce_api_objects_userrecordaccess.htm

I was writing a VF page in which i needed to have a table of sobject records with Edit and del links for each line item. I was using{!URL for} function which would take care of the link automatically. It would even prevent users who do not have permission to edit or delete the record to take it to the page 'you do not have sufficient permission'.

 I thought it would be nice to know if the loggedin user or profile permission can be queried based on which i could display or hide these links instead of user clicking the link and finding out. Thats when i searched i figured it could be handled with Api 24.0

SELECT RecordId, HasReadAccess, HasTransferAccess, MaxAccessLevel
     FROM UserRecordAccess
     WHERE UserId = [single ID]
     AND RecordId = [single ID]      //or Record IN [list of IDs]

so i needed to get info on hasEditAccess and hasDeleteAccess for loggedin user and based on which i displayed or hid the link. It worked like charm.

Wednesday, March 28, 2012

Sort an Apex class Object List

[Edit : Now there is an interface natively provided by salesforce to sort the class object lists]

I recently needed to sort an apex class object List which was retrieved through a webservice callout as it could not be sorted at the service end.

This is the apex method which was used to build it

 public void testsort(){
        map<string,List<accClass>> stracc = new map<string,list<accClass>>();
        for(accClass a:accClassobj ){
            if(stracc.containsKey(a.name))
                stracc.get(a.name).add(a);           
            else
              stracc.put(a.name,new accclass[]{a});          
        }
        accclassobj= new list<accclass>();
        list<string> nameList = new list<string>();
        nameList.addAll(stracc.keyset());
        nameList.sort();       
        for(string s:nameList){
           accclassobj.addAll(stracc.get(s));          
        }

Steps involved in sorting

1. build a map with object parameter as the key by which we gonna sort and List<object> as the value.
2. sort the keyset
3. arrange the values based on the sorted keyset, so that object gets sorted by that parameter

It is a map of parameter and list of object but not parameter and just object to handle the records which will have same parameters. This sorting handles null values automatically. This sorting can never be dynamic and you will need to create more methods based on the object parameter by which sorting is needed.

Friday, March 23, 2012

Filter list alphabetically



There was a requirement where i needed to build a list which can be filtered based on the starting alphabet. And also the List needs to remember the selections made as we move through other pages.

<apex:page controller="Pagin_alpha">
<style type="text/css">
      .loadingIcon {
            background-image: url(/img/loading.gif);
            width: 16px;
            height: 16px;
        }
     </style>
<script type="text/javascript">
function checkAll(cb,cbid)
        {
            var inputElem = document.getElementsByTagName("input");                     
            for(var i=0; i<inputElem.length; i++)
            {  
               
                 if(inputElem[i].id.indexOf(cbid)!=-1){                                        
                inputElem[i].checked = cb.checked;
                }
            }
        }
</script>
<apex:form id="form">
<br/>
<div style="align:right;">
<apex:repeat value="{!alphabet}" var="a">
<apex:commandLink value="{!a}" action="{!refreshList2}" rerender="form" style="{!if($CurrentPage.parameters.alpha=a,'font-weight:bold','')}" status="stat">
<apex:param name="alpha" value="{!a}"/>
</apex:commandLink>
&nbsp;|&nbsp;
</apex:repeat>
</div>
<br/>
<apex:pageBlock id="block">
<apex:pageBlockButtons >
<apex:commandButton rendered="{!standardsetcon.hasprevious}" value="Previous" action="{!standardsetcon.previous}" rerender="block,block2" status="stat"/>
<apex:commandButton rendered="{!standardsetcon.hasnext}" value="Next" action="{!standardsetcon.next}" rerender="block,block2" status="stat"/>
</apex:pageBlockButtons>
<apex:actionStatus id="stat">
<apex:facet name="start">
<apex:outputPanel layout="block" styleClass="message infoM4">
<apex:panelGrid columns="2" styleClass="messageTable" columnClasses="messageCell" style="padding:0px;margin:0px;">
<apex:panelGroup >
<img class="loadingIcon" src="/s.gif"/>
</apex:panelGroup>
<apex:panelGroup >
<div class="messageText">Please wait...</div>
</apex:panelGroup>
</apex:panelGrid>
</apex:outputPanel>
</apex:facet>
<apex:facet name="stop">
<apex:pageBlockTable value="{!CurrentList}" var="c" id="table">
<apex:column >
<apex:facet name="header"><apex:inputcheckbox onclick="checkAll(this,'check')" value="{!fals}" /></apex:facet>
<apex:inputcheckbox value="{!c.checked}" id="check">
</apex:inputcheckbox>
</apex:column>
<apex:column value="{!c.ocontact.name}" headerValue="Name"/>
</apex:pageBlockTable>
</apex:facet>
</apex:actionStatus>
</apex:pageBlock>
<apex:pageBlock title="Selected Contacts" id="block2">
<apex:commandButton value="Display Selected" rerender="block2"/>
<apex:pageBlockTable value="{!DisplaySelectedList}" var="c">
<apex:column value="{!c.name}" headerValue="Name"/>
<apex:column value="{!c.phone}" headerValue="Phone"/>
</apex:pageBlockTable>
</apex:pageBlock>

</apex:form> 
</apex:page>

In the VF page i am displaying the List 'CurrentLsit' which would hold the contact list with checkbox which is paginated and 'DisplaySelectedList' which would remember the selected contact. Array Alphabet is used in repeat to build the link of alphabets at the top of page

Controller method



public with sharing class Pagin_alpha {

    
    private List<contactSet> contactSetList{get;set;}
    private string contactListQuery;
    private set<contact> selectedContact;
   
    public List<string> alphabet{get;set;}
    public boolean fals{get;set;}  
   
    public Pagin_alpha(){
      fals=false;
      alphabet=new string[]{'All','a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; 
      contactSetList = new List<contactSet>();
      selectedContact = new set<contact>();
      contactListQuery= 'select id,name,phone from contact limit 1000';   
    }
   
     Private void updateSelectedContact(){
        for(contactSet cs:contactSetList ){
           if(cs.checked)
               selectedcontact.add(cs.ocontact);
           else{
               if(selectedcontact.contains(cs.ocontact))
                   selectedcontact.remove(cs.ocontact);
               }
       }  
    }
   
      public ApexPages.StandardSetController standardSetCon {
        get {
            if(standardSetCon == null) {
                standardSetCon = new ApexPages.StandardSetController(Database.getQueryLocator(contactListQuery));
                // sets the number of records in each page set
                standardSetCon .setPageSize(20);
            }
            return standardSetCon ;
        }
        set;
    }
 
    public List<contactSet> getCurrentList() {
       updateSelectedContact();
       contactSetList = new List<contactSet>();      
        for (contact category : (List<contact>)standardSetCon.getRecords()){       
            if(selectedContact.contains(category))         
            contactSetList.add(new contactSet(category,'true'));
            else
            contactSetList.add(new contactSet(category));
        }
        fals=false;
        return contactSetList;
    }
   
    public PageReference refreshList2() {      
       standardSetCon = null;    
       string s;
       if(apexpages.currentpage().getparameters().get('alpha') == 'All')
           s='%';
       else
           s= apexpages.currentpage().getparameters().get('alpha')+'%';
      
       contactListQuery= 'select id,name,phone from contact where name like' +'\''+s +'\''+ 'limit 5000';          
        return null;
    }
   
  
   
    public List<contact> getDisplaySelectedList(){
        updateSelectedContact();
        List<contact> displaycon = new list<contact>();
        displaycon.addall(selectedcontact);
        return displaycon;
    }
      
    public class contactSet {
        public Boolean checked{ get; set; }
        public contact ocontact { get; set;}

        public contactSet(){
            ocontact = new contact();
            checked = false;
        }
        public contactSet(contact c){
            ocontact = c;
            checked = false;

        }
        public contactSet(contact c,string s){
            ocontact = c;
            checked = true;

        }
    }
   
 
}
getCurrentList populates the currentList in the page. refreshList2 will refresh the list when the link on top of the page to filter the contact is clicked.

This concept can be extended to multiple fields in the List .

Saturday, March 10, 2012

Hierarchy in VisualForce

This is an example of how to build a hierarchy Lists in VF page with select checkboxes. It will have a checkbox at parent level with which you can select all the child automatically. It has a neat link with which you can show or hide children under a parent row. It uses jquery libraries for achieving this. It has a list of accounts with each accounts displaying all its contacts in a hierarchy view.

Apex Class

public class accconhierarchy {
    Private list<account> acc;
   
    public List<accountWrap> accountList { get; set; }
    public accconhierarchy (){
        List<contactWrap> cw;
        accountList = new List<accountWrap>();
        acc = [select id,name,industry,billingcountry,createdbyid,(select id,name,email,phone from contacts) from account limit 20];
        for(account a:acc){
            cw = new list<contactWrap>();
            For(contact co : a.contacts){
               cw.add(new contactWrap(co));
            }
            accountList.add(new accountWrap(a,false,cw));
        }
    }
   
    public class accountWrap{
        public account oAccount{get;set;}
        public boolean isSelected{get;set;}
        public List<contactWrap> contactset{get;set;}
        public accountWrap(account a,boolean b, List<contactWrap> c){
            oAccount=a;
            isSelected=b;
            contactset =c;           
        }
    }
   
    public class contactWrap{
        public contact oContact{get;set;}
        public boolean isSelected{get;set;}       
        public contactWrap(contact a){
            oContact=a;
            isSelected=false;                      
        }
    }
   
}


Visualforce Page

<apex:page controller="accconhierarchy">
  <apex:form >
      <apex:pageBlock >
      <style type="text/css">
        .showContacts {
            background: transparent url('/img/alohaSkin/twisty_sprite.png') 0 0 no-repeat;
        }
        .hideContacts {
            background: transparent url('/img/alohaSkin/twisty_sprite.png') 0 -11px no-repeat;
        }
    </style>
                  <apex:outputPanel layout="none"
                        rendered="{!accountList.size!=0}">
                        <table id="tableAccount" class="list" border="0" cellpadding="0" cellspacing="0">
                            <thead>
                                <tr class="headerRow">
                                    <th class="actionColumn">Action</th>
                                    <th>Account Name</th>
                                    <th>Industry</th>
                                    <th>Billing Country</th>
                                    <th>Createdby</th>
                                </tr>
                            </thead>
                            <tbody>
                            <apex:repeat id="repeatAccount"
                                value="{!accountList}"
                                var="item">
                                <apex:variable var="acc" value="{!item.oAccount}" />  
                                <tr id="{!$Component.repeatAccount}:account" class="dataRow">
                                    <td class="actionColumn">
                                        <apex:inputCheckbox id="selectAccountRow"
                                            value="{!item.IsSelected}"
                                            onclick="javascript:toggleSelectAll(this, '{!$Component.repeatAccount}');"/>
                                    </td>
                                    <td class="dataCell">
                                        <apex:outputLink id="linkShowHide" value="javascript:toggleContactRow('{!$Component.repeatAccount}')">
                                            <img id="{!$Component.repeatAccount}:Img" src="/s.gif" class="showContacts" border="0" height="11px" width="11px" style="padding-right: 4px;"/>
                                            <apex:outputField value="{!acc.Name}" />
                                        </apex:outputLink>
                                    </td>
                                    <td class="dataCell"><apex:outputField value="{!acc.Industry}" /></td>
                                    <td class="dataCell"><apex:outputField value="{!acc.billingcountry}" /></td>
                                    <td class="dataCell"><apex:outputField value="{!acc.CreatedById}" /></td>
                                </tr>

                                <tr id="{!$Component.repeatAccount}:Contact" class="dataRow" style="display:none;">
                                    <td colspan="100" style="padding:10px; padding-left: 45px;">
                                        <h3>Contacts</h3>
                                        <br/>                                       
                                        <apex:outputPanel layout="none">                                           
                                            <table id="tableAccountContacts" class="list" border="0" cellpadding="0" cellspacing="0">
                                                <thead>
                                                    <tr class="headerRow">
                                                        <th class="actionColumn">Action</th>
                                                        <th>Name</th>
                                                        <th>Email</th>
                                                        <th>Phone</th>
                                                    </tr>
                                                </thead>
                                                <tbody>
                                                <apex:repeat id="repeatAccountContacts"
                                                    value="{!item.contactSet}"
                                                    var="subitem">
                                                    <apex:variable value="{!subitem.oContact}" var="con" />
                                                    <tr class="dataRow">
                                                        <td class="actionColumn"><apex:inputCheckbox id="selectContactRow" styleclass="ContactCheckbox" value="{!subitem.IsSelected}"/></td>
                                                        <td class="dataCell"><apex:outputField value="{!con.Name}" /></td>
                                                        <td class="dataCell"><apex:outputField value="{!con.email}"/></td>
                                                        <td class="dataCell"><apex:outputField value="{!con.phone}" /></td>                                                       
                                                    </tr>
                                                </apex:repeat>
                                                </tbody>
                                            </table>
                                        </apex:outputPanel>
                                    </td>
                                </tr>
                            </apex:repeat>
                            </tbody>
                        </table>
                    </apex:outputPanel>
 <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
 <script>
        jQuery.noConflict();
        function toggleContactRow(rowId) {
            try {
                var AccountImg = jQuery(document.getElementById(rowId + ":Img"));
                var ContactsRow = jQuery(document.getElementById(rowId + ":Contact"));
                if (AccountImg.hasClass("hideContacts")) {
                    AccountImg.attr("class", "showContacts");
                    ContactsRow.hide();
                }
                else {
                    AccountImg.attr("class", "hideContacts");
                    ContactsRow.show();
                }
            }
            catch (e) { alert(e); }
        } 

        function toggleSelectAll(elCheckbox, rowId) {
            try {
                var SelectAll = jQuery(elCheckbox);
                var ContactsRow = jQuery(document.getElementById(rowId + ":Contact"));
                ContactsRow.find(".ContactCheckbox").each(function() {
                    jQuery(this).prop("checked", SelectAll.is(":checked"));
                });
            }
            catch (e) { alert(e); }
        }

    </script>
    </apex:pageBlock>
  </apex:form>
</apex:page>

JS function toggleContactRow is used to show or hide contacts and toggleSelectAll is for header level checkbox at the parent Level.

Wednesday, February 22, 2012

SFDC API 24.0 test failure issue

Yesterday I was experiencing a weird issue.

For a new requirement we did development in our DEV environment…We wrote new test methods and we thought the work is done :)... 
However, there were Null Pointer Exceptions when we ran the test methods.
And it was pointing to the custom settings….we doubled checked and confirmed that the custom setting had the default values. Also, it was accessible to anyone (public).

After some time I realized that the test method API version was 24.0.. I changed it to 23.0 and there were no errors :)

Edit - SFDC has introduced test classes not to access data directly with API 24. Even though it is a good move , I would like to access certain info by default like custom settings, and Pricebook. But there is always an option to use @istest(seealldata=true) to access live data.


http://boards.developerforce.com/t5/General-Development/Custom-setting-not-accessible-from-test-class/m-p/399669#M66636

Saturday, February 4, 2012

Account address to be displayed as single block

Small code snippet to display Account address as a single block just like in standard page

<apex:outputText label="Shipping Address"
                    escape="false"
                    value="{!IF(NOT(ISNULL(Account.ShippingStreet)),'{0}<br/>{1}, {2} {3}<br/>{4}', '&nbsp;')}">
                   
                    <apex:param value="{!Account.ShippingStreet}" />
                    <apex:param value="{!Account.ShippingCity}" />
                    <apex:param value="{!Account.ShippingState}" />
                    <apex:param value="{!Account.ShippingPostalCode}" />
                    <apex:param value="{!Account.ShippingCountry}" />
                </apex:outputText>

Wednesday, January 25, 2012

Identifying SobjectType Dyanamically

getKeyPrefix of Schema.getGlobalDescribe can be used to identify dynamically the SobjectType in Apex.

String prefix = recordId.substring(0,3);
String objectType;
Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();
for(Schema.SObjectType s : gd.values()){
    if(prefix == s.getDescribe().getKeyPrefix()){
        objectType = s.getDescribe().Name;
    }
}

recordId consists the Id of the sobject Record for whose SobjectType needs to be identified. Above piece of code does it well.

Wednesday, January 11, 2012

Build your Own Standard set controller

Standard set controller can be used for many objects . Recently i found that it cannot be used with activity objects(Open activity and activity history). Also there are many other Restrictions placed on these objects. You cannot query on these objects directly. It has to be a nested SOQL within an object using a relationship name.

So coming back to my requirement i needed to roll up related list for all the activities in the account hierarchy for the topmost parent. For other related list i achieved it through standardset controller. But for activity alone i created a class which would behave like a standard set controller.

public class SetActivityClass {
        public integer pagenumber{get;set;}
        public integer totalpages{get;set;}
        public integer pagesize{get;set;}
        public list<sobject> sobjectList{get;set;}       
 public SetActivityClass(list<sobject> sobjectList){
        pagenumber =1;
        pagesize = 60;
        this.sobjectList=sobjectList;
        totalpages= Integer.valueof(math.ceil((double)sobjectList.size()/(double)pagesize ));
        }
        public boolean gethasprevious(){
          return (pagenumber != 1);
        }
        public boolean gethasnext(){
            return (pagenumber != totalpages);
        }
        public pagereference Previous(){
        if(gethasprevious())
            pagenumber--;
            return null;
        }
        public pagereference next(){
        if(gethasnext())
            pagenumber++;
            return null;
        }
        public list<sobject> getRecords(){
        list<sobject> Sactivity = new list<sobject>();       
            for(integer counter2=(pagenumber-1)*pagesize;counter2<(math.min(pagenumber*pagesize,sobjectList.size()));counter2++){
                Sactivity.add(sobjectList.get(counter2));
            }
            return Sactivity;
        }
          
    } 

oActivityHistory= new SetActivityClass(ActivityHistoryList);
        oOpenActivity= new SetActivityClass(OpenActivityList); 


public List<OpenActivity> getGoToListOpenActivities() {
    return oOpenActivity.getRecords();       
    }

public List<ActivityHistory> getGoToListActivityHistories() {
    return oActivityHistory.getRecords();       
    }

Just like the standard set controller, this SetActivityClass has methods to return next set or previous set of records. These can be added to command links as they are pagereference method. Also Hasprevious and Hasnext can be called from the page to check if the previous or next set of records exist.Pagenumber denotes the current page number. Pagesize determines no of records to be displayed in the page. I would recommend a size of 40 -60 in a page for better readability by Users(I have hardcoded it as 60). Total pages is calculated based on this pagesize.

Also note that getRecord function accepts and returns generic sobject. I used the same class for both activityhistory and openactivity.This Class can be slightly tweaked to handle list of objects of your custom class  as well.

Here is how i would display it in the page

<apex:panelGrid columns="2">                 
 <apex:commandLink action="{!oOpenActivity.previous}" rendered={!oOpenActivity.hasPrevious}"> Previous </apex:commandLink>
  <apex:commandLink action="{!oOpenActivity.next}" rendered="{!oOpenActivity.hasNext}" >Next </apex:commandLink>           
  </apex:panelGrid>

<apex:pageBlockTable id="tableMasterAccountOpenActivities" var="openact"  value="{!GoToListOpenActivities}">
 <apex:column .....
.......
</apex:pageblocktable>

And whenever the next and previous is click make sure atleast the pageblock tabele is rerendered if not the whole page based on your logic. I  have shown the VF for openactivity. similarly u can use it for activity history. Also you can use the same logic in building your own custom class wrapper object standard set controller this way.

Saturday, January 7, 2012

Javascript for Ghost Text in Salesforce

There will be a need to add ghost text which will be like a help text or format in which the value needs to be there for a particular input field in visual force. I would suggest use visual force help text for it. But if ghost text is required use the following script.



Trick here use the script after all the component is loaded preferably before </apex:page> tag.

        <script type="text/javascript">  
            var txtContent  = document.getElementById("{!$Component.theform.thepb.thepbs.mp}");
            var defaultText = '+91 - *** *** ****';
            txtContent.value = defaultText;           
            txtContent.style.color = '#CCC';           
            txtContent.onfocus = function() {
              if (this.value == defaultText) {   
                this.value = '';
                this.style.color = '#000';
              }
            }
            txtContent.onblur = function() { 
              if (this.value == '') {   
                this.value = defaultText;
                this.style.color = '#CCC';
              }
            }
        </script>  

Also Make sure to handle the save if the field is filled with ghost text(Default text here).

Friday, December 9, 2011

Cool links for salesforce.com tools and best practices

I was going through the Best practices for force.com and Trigger template. It will be really helpful if we could follow it in our code.

You can also find lots of interesting tools for salesforce.com here.

Found this awesome IDE and data loader on the cloud !!! - Workbench . I would have loved to have metadata export on this. If anyone knows a tool not an app through which we can export metadata (not like an XML) more like a spreadsheet which can be used for documentation and data mapping please drop a comment here.

Finally Vote for all the ideas in this link. These are all the features worth having in SFDC.

There are many a times where we need to mimic a standard page through VF. Creating them to mimic it is a hard thing. But it can be done with a single click , whole code gets generated. You can check that out here. Pretty cool stuff aint it.

Tuesday, October 18, 2011

Javascript to select all checkboxes in visualforce page

I needed to develop a custom visualForce page which would hold a page block table which would contain a checkbox and details of a custom object based on which user can select and click on buttons to perform some operations.

I wrapped the custom object record and check box in a wrapper class. I needed a simple functionality when the header checkbox is selected or deselected accordingly all the checkbox will get selected or deselected accordingly. So i looked into community and first result community gave is this(controller way). It involved calls to back end and as a result functionality is little slow.

So I went for a Javascript solution which would provide a faster,elegant and much simpler way.Here is the VF code which was used in the page above.

<apex:pageBlockTable value="{!wrapreportsobj }" var="w" id="Selected_PBS">
<apex:column ><apex:facet name="header">
<apex:inputCheckbox onclick="checkAll(this,
'checkedone')"/>
</apex:facet>
<apex:inputCheckbox value="{!w.selected}" id="checkedone"/></apex:column>
<apex:column ><apex:facet name="header">Id</apex:facet><apex:outputfield value="{!w.rep.id}"/></apex:column>
<apex:column ><apex:facet name="header">Name</apex:facet><apex:outputfield value="{!w.rep.name}"/></apex:column>
</apex:pageBlockTable>

Note that I have set the id of the checkbox as checkedone and while clicking on header checkbox it calls a javascript function  checkAll. Giving an id will segregate all the checkboxes in the table. Here is the javascript which would handle the selecting and deselecting all of the checkboxes which has id checkedone in it.


<script type="text/javascript">
    function checkAll(cb,cbid)
        {
            var inputElem = document.getElementsByTagName("input");                    
            for(var i=0; i<inputElem.length; i++)
            {            
                 if(inputElem[i].id.indexOf(cbid)!=-1){                                       
                    inputElem[i].checked = cb.checked;
                }
            }
        }
</script>


Friday, September 16, 2011

Migrating Opportunity line item

This post is a continuation of my earlier post on Product, Pricebook and Pricebook entry.Opportunity Product(line item) is a junction object between opportunity and pricebookentry table.

List of fields in opportunity products
  • ID
  • OPPORTUNITYID
  • PRICEBOOKENTRYID
  • CURRENCYISOCODE
  • QUANTITY
  • TOTALPRICE
  • UNITPRICE
  • LISTPRICE
  • SERVICEDATE
  • DESCRIPTION
Fields indicated in red are mandatory fields and fields which are in grey are read only. Unit price field specifies the price at which the product was sold. List price is the price of the product in the pricebook. use either one of the totalprice or unitprice field while loading.The other field will be automatically calculated based on quantity while loading. Refer to my previous post for inserting a new pricebook entry/ Exporting existing pricebookentry. Prepare a csv file with all these data.Also optional fields like service date and description can be filled for CSV. Use data loader to insert data. Click here for Sample CSV

Notes on loading

  • pricebook entry should be in the same pricebook that is assigned to the opportunity.  The pricebook name can be found in pricebook2id field in opportunity
  • If the opportunity does not have an associated pricebook2, the API automatically sets the pricebook on the opportunity if the line item corresponds to a Pricebookentry in an active Pricebook2. Changing the pricebook association would delete existing opportunity line items.
  • Creating an OpportunityLineItem increments the OpportunityAmount value by the TotalPrice of the OpportunityLineItem. Additionally, inserting an OpportunityLineItem increments the ExpectedRevenue on the opportunity by the TotalPrice times the opportunity Probability.
  • The OpportunityAmount becomes a read-only field when the opportunity has line items.
  • If multi-Currency is enable for your organization, Choose a price book that contains products with the same currency as the opportunity. pricebook entry currency code should match opportunity currency code.You cannot update the CurrencyIsoCode field on the opportunity if line items exist.

Thursday, September 8, 2011

Escalation Queues and workflows.

I recently had a need to escalate a case which has status value new and in a queue for 2 hours, So that if nobody picks up the case it would be escalated to manager.

It took me about a day to figure out how to write the entry criteria to check whether the case is in a queue or assigned to a owner. salesforce Ids has a certain pattern across all org for certain objects. so by that i figured out that users always started with 005 and group and queue always started with 00G.

Also i had to use 'Formula evaluates to true'  instead of easy 'Criterias are met' as owner ids are not available in this. Here is the formula i used.

AND(  ISPICKVAL( Status ,"New") , BEGINS(OwnerId, "00G") ,not( IsClosed ) )  

 Now coming to other stuffs
  
Realized that Case Escalation cannot trigger a workflow. Case escalation would cause an 'isEscalated' flag to be checked. I assumed that it can be used to trigger a workflow. i used the same entry criteria in workflow as in the escalation rule and used time dependent action instead. Thankfully my use case needed field updates only for the first time the case is escalated.


Couple of more things changes to formulae field would not trigger the workflow and so also field update from a workflow. This link might be useful Workflow considerations

If you could find if salesforce has documented somewhere which all will trigger a workflow, Let me know 

Tuesday, August 23, 2011

Loading Converted Leads

      When you are migrating data from one system to another there will be a need to load the leads as converted as they would have been already converted in the source system.

    First step is to raise the case with salesforce.com to open up the audit fields for your organisation. The fields CONVERTEDACCOUNTID, CONVERTEDCONTACTID, CONVERTEDOPPORTUNITYID and CONVERTEDDATE will not be visible while inserting or updating by default.

Once the salesforce allows you to load these system fields you can start your process.

Steps for Loading
  • Firstly, Load Account, Contact and Opportunity which needs to be associated with the converted lead.
  • Next prepare a csv files with information of lead and get the ids from previous step and load it into the appropriate converted fields of file as mentioned above.
  • The field Isconverted should be set to true
  • Insert, Update or Upsert the leads as necessary.
    Things to consider while loading

    • Converted Account is mandatory . Converted contact and opportunity is not mandatory. converted contact and opportunity need not belong to converted account.
    • You cannot see the information of converted leads. If you go to the lead detail page through its id you will see information on Converted date, Converted account, Contact and opportunity whichever was mapped. Only way to extract the information is through API
    • Lead conversion mapping will not be respected.For eg Lead description is mapped to contact description and when u load the converted lead with description it will not be carried over to the converted contact.
    • Converted date if not mapped will give value This lead was converted on {0} on lead detail page. If you need to enter converted date make sure you also enter created date . Otherwise it would error out as created date would be defaulted to now which will be at future date when compared to converted date.
    • Lead triggers and validation rules will be fired.
    • Converted Leads cannot be updated but can be deleted 
    click here to have a look at the sample csv file.
      If you have any questions please post it in comments section