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

      Thursday, August 18, 2011

      Rendering VF page as pdf and sending pdf as an attachment from a list view

      Thought i would share something which i needed to work on. I thank Jeff Douglas for his awesome post which helped me in this assignment.

      Requirement
          There is a custom visualforce page which is rendered as pdf. There is a button on contact detail page which when clicked would call this vf page. These things were present. In addition to viewing this pdf from contact detail there was also a necessity to email these PDFs that too in bulk from list views.

      Solution
      list button was created with source as VF page.

      <apex:page standardController="contact" recordSetVar="Props" extensions="emailcontact" action="{!sendingemail}">
      </apex:page>

      Controller is where all the code is

      public  class emailcontact {
           public ApexPages.StandardSetController propcontroller{get;set;}
          public list<contact> props{get;set;}  
          public emailcontract(ApexPages.StandardSetController controller) {
          propcontroller =(ApexPages.StandardSetController)controller;   
          }
          public pagereference sendingemail(){
              props = propcontroller.getSelected();// Contains the list of record which is selected from the list  
               
              PageReference pdf = Page.VFpagetoberenderedaspdf;
              for(contact pr : props)
              {
                  pdf.getParameters().put('id',pr.Id);
                  attachment attach = new attachment();
                  Blob body;
                  try {
                      body = pdf.getContentAsPDF();
                      }
                  catch (VisualforceException e) {
                      body = Blob.valueOf('Missing Text');
                      }
                  // ********************* for attachment to the contactrecord ******************************   
                  try{
                  attach.Body = body;        
                  attach.Name = 'Contact' +system.today+'.pdf';
                  attach.IsPrivate = false;
                  attach.ParentId = pr.Id;
                  att.add(attach);
                  }
                  catch(exception e)
                  {
                 
                  }
                  // ************************ for email creation************************************   
                 
                  Messaging.EmailFileAttachment attach2 = new Messaging.EmailFileAttachment();
                  attach2.setContentType('application/pdf');
                  attach2.setFileName('Contact.pdf');
                  attach2.setInline(false);
                  attach2.Body = body;
                  Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                  String[] toAddresses = new String[] {pr.Email};
                  mail.setToAddresses(toAddresses);
                  mail.setSubject('Offer for' + pr.name);// alternatively use templates as well
                  mail.setFileAttachments(new Messaging.EmailFileAttachment[] { attach2 });
                  mail.setHtmlBody('Please find attached a cash offer for your listing at ');           
                  em_sent.add(mail);          
              }
          }
          if(att!= null)
          insert att;
          if(em_sent != null)
          Messaging.sendEmail(em_sent);         
          pagereference pr  = new pagereference('/a0e/o');//will redirect to contact tab
          return pr;
          }
      }

      Also you can add a piece of code to send an email to yourself to indicate the success or failure of the action or display it in the page

      Limitations

      Major limitation i faced is no of the records the code can process (Governor limits on callouts). It was limited to 10 after which time out error happened. I tried using batch as well as future calls. Both returned me empty PDFs. Waiting for Salesforce to remove these limitations in future.

      Setting a child record to play a unique primary role to parent through config

      There are many use cases where there is need to set one of the child record as primary and when another child record is updated as primary it should throw up an error saying that primary record already exists (similar to contact roles function). Many of  the developers would suggest an apex trigger solution But this can be achieved through configuration alone.

      Its a simple 2 step process.

      1. Create a Roll up summary field on the parent object. It would roll up the count of the child records which have the primary checkbox set to true.
      2. Create a validation rule on this field if the number becomes more than 1 validation error will be thrown.
      U are good to go now. When the child record is updated to primary rollup would happen. But when there is a primary record rollup will try to become 2 triggering the validation error thus preventing it from saving the record. But if the relationship between child and parent is lookup or u want user to be pointed to the primary record in the error message listen to developers :) 

      Loading products , pricebooks and pricebook entry

      There is always a need to load Products into salesforce. Its a 3 step process to load products and associate them with pricebook . There is a standard pricebook which salesforce provides by default which can be deactivated. There is also an option to create custom pricebook as many as you want in accordance with the business.


      Fields in red are required and fields in blue are read only



      Pricebookentry is the junction object between product2 and pricebook2 object( For strange reasons they have suffix 2 for standard object) . It has lookup for product2 and pricebook2 field through product2id and pricebook2id field. Name and productcode is copied over from product2 based on product2id field.

      Essentially for loading Products you need to load product2 table first. Create a CSV file. Name is the only required field. Optionally have columns for other fields indicated in the pic.set Isactive to true. use dataloader to insert data. Get the id of the products from success file which got created.

      Pricebook2 will also have only Name as required field. Isstandard indicates whether its a standard or custom and its readonly. Also Isactive should be set to true if you are planning to use it. Get the Pricebook2id from success file. Alternatively export pricebook2 table to get id of pricebook already created.

      Final step is to create pricebookentry. Pricebook entry needs to be created for each Pricebook.
      populate ids of product2 in product2id column and pricebook2 in pricebook2id column. Unitprice is a required field where the price for this pricebook is set. Pricebook entry for a custom pricebook cannot be made if that product is not there in the standard pricebook.

      Unitprice can be made read only if usestandardprice field is set to true. This can be made only for custom pricebook and when the product is added to standard pricebook. So the unitprice set in standard pricebook will be carried over to the custom pricebook. Make Isactive true whenever u are planning to use it. Use dataloader to insert the pricebookentry. you have to repeat the above 2 steps for each custom pricebook.If you have multiple currencies enabled there should be as many pricebook entry records for a single product in a pricebook for each currency.