Wednesday, April 11, 2012

Generating Salesforce.com Quotes PDF with neat page breaks

I love Salesforce, but I fail to understand why the basic functionality like generating a presentable quotes PDF is not part of the standard out of the box functionality. Many of you may disagree because, salesforce.com does provide templates to modify the quotes. 

But if you look at it closely, the page breaks are not consistent, if my product description is a little too long, the quote extends to multiple pages, without proper page breaks. So, I searched the internet for few solutions and found 1 good solution on the developer force, to dynamically (almost ;-) ) populate pages with the number of quotes. However, again the drawback is that if the description is too long, the page breaks will not be consistent and the quote will look ugly. 

The final solution I came up with is an extension to an already existing solution on developer force. Though this is not a neat solution, but I guess we have to live with these kind of solutions until Salesforce comes up with a better out of the box quote generation functionality. 

As you would expect, there are 2 components - 
1. Visual Force Page to generate the pdf. 
2. An extension to return the quotes. 


Visual Force Page

<div style="page-break-after:always;"> 
     </div>
   <apex:repeat value="{!pageBrokenQuoteLines}" var="aPageOfQuotes" id="theList">
         <div style="page-break-after:always;"> 
<apex:dataTable value="{!aPageOfQuotes}" var="c" id="theTable">
<apex:column > 
<apex:facet name="header">Qty</apex:facet>
<apex:outputText value="{!c.Quantity}"/>
</apex:column>
<apex:column >
<apex:facet name="header" >Part Number</apex:facet>
<apex:outputText value="{!c.PricebookEntry.Product2.Name}"/>
</apex:column>
<apex:column ></apex:dataTable>

Extension

public with sharing SalesQuoteExample{
public List<QuoteLineItem[]> pageBrokenQuoteLines {get; private set; }
    
  // end new code
  
  public Quote quote { get; private set;} 
    public QuoteLineItem[] quoteLineItems { get; private set; }
    public Opportunity opp { get; private set; }  
    public Boolean hasContact { get { return (opp.opportunityContactRoles.size()  == 1);} }
    public Contact contact { get; private set; }  

    private ApexPages.StandardController controller;
    
      // constructor, loads the quote and any opportunity lines
    void queryQuoteLines(id id) { 
      quote = [  Select s.Contact.Name, Contact.Account.Name, s.Contact.MailingStreet, s.Contact.MailingState,
          s.Contact.MailingPostalCode, s.Contact.MailingCountry, 
        s.Contact.MailingCity , s.Contact.Phone, S.Email,
           (Select Quantity, PricebookEntry.Product2.Name, ListPrice, Adjusted_Price__c, 
           Product_Description__c, TotalPrice, LineNumber  From QuoteLineItems
           order by PricebookEntry.Product2.Name ), 
       s.Opportunity.HasOpportunityLineItem, s.Opportunity.Name, s.Name, s.QuoteNumber, 
       s.Opportunity.Id, s.OpportunityId
       
         From Quote s 
       where s.id = :quoteid limit 1]; 
      quoteLineItems = quote.QuoteLineItems; 
      prepareQuoteLinesForPrinting();
    }
    id quoteid; 
    /*public SalesQuotes() {
      quoteid = ApexPages.CurrentPage().getParameters().get('id');
      init(); 
    }*/
    public SalesQuotes(ApexPages.StandardController c) {
      controller = c;
      quoteid = c.getRecord().id;
      init();
    } 
    
    // load up quote lines, opportunity lines, opportunity details and contact info
    void init() { 
     queryQuoteLines(quoteid);
  }

    /* The action method that will generate a PDF document from the QuotePDF page and attach it to 
       the quote provided by the standard controller. Called by the action binding for the attachQuote
       page, this will do the work and take the user back to the quote detail page. */
    public PageReference attachQuote() {
        /* Get the page definition */
        PageReference pdfPage = new PageReference( '/apex/quotePDFAdvanced' );
        
        /* set the quote id on the page definition */
        pdfPage.getParameters().put('id',quote.id);
        
        /* generate the pdf blob */
        Blob pdfBlob = pdfPage.getContent();
        
        /* create the attachment against the quote */
        Attachment a = new Attachment(parentId = quote.id, name=quote.name + '.pdf', body = pdfBlob);
        
        /* insert the attachment */
        insert a;
        
        /* send the user back to the quote detail page */
        return controller.view();
    }
   
  PageReference opportunityPR() { return new pagereference('/'+quote.OpportunityId); }
  PageReference quotePR() { return new pagereference('/'+quote.id); }
  
   
  public PageReference reset() { 
    queryQuoteLines(quote.id);
    return null;
  }
    //splits the quote lines into an approximate number of rows that can be 
    //displayed per page
   private void prepareQuoteLinesForPrinting()
   {
      pageBrokenQuoteLines = new List<QuoteLineItem[]>();
      Integer linesAvailable = 60;
      
     
     QuoteLineItem[] pageOfQuotes = new QuoteLineItem[]{};
     Integer counter = 0;
     boolean firstBreakFound = false;
     
     //Additional variables
     String description;
     String productName;
     Integer descLines=0;    
     Integer lines; 
     
     for(QuoteLineItem q : quoteLineItems)
     {
       
       System.debug('Quote Line Item is ****'+q.LineNumber);
       description = q.Product_Description__c;
       if(description!=null)
       {
           descLines = description.length()/40;
           System.debug('Lines Available::::'+linesAvailable);
           System.debug('Description Length==='+description.length());
           System.debug('descLines+++'+descLines);
           if(linesAvailable > descLines)
           {
               System.debug('Lines Available');
               linesAvailable = linesAvailable - descLines;
               pageOfQuotes.add(q);
               descLines = 0;
           }
           else
           {
               System.debug('NO Lines Available::::'+linesAvailable);
               pageBrokenQuoteLines.add(pageOfQuotes);
               pageOfQuotes = new QuoteLineItem[]{};
               descLines = 0;
           }
           
       }
    }
     //if we have finished looping and have some quotes left lets assign them
     if(!pageOfQuotes.isEmpty())
     {
       pageBrokenQuoteLines.add(pageOfQuotes);
     }
   }  

}

The method  prepareQuoteLinesForPrinting  is based on the following assumptions
  1. The description will have approximately 40 characters per line.
  2. A page can accommodate 50 lines of quotes.   Can be changed by specifying a new value in the variable.
The logic of the for the method is 
  1. For each quote line item, calculate the description length.
  2. Divide this length by 40, to get an approximate number of lines this quote line item will take. 
  3. Subtract the value from above from the available lines. Available lines indicate the space left in the page to display quote line item.
  4. If the available lines is greater than the description lines(indicating space is available), add this quote on the current page.
  5. Else, add the quote to new page, reset the available lines to 50 (this is configurable).
Except for the logic I have mentioned above, this code is available on the following website, however, you will need to include the logic I have mentioned if you want to have cleaner page breaks


Credits
Authentic Blog, featured by BlogUpp

Copy multi-select picklist in Salesforce

I am pretty sure, you would have come across a situation where you wanted to copy the multi-select picklist through a workflow rule. Unfortunately salesforce does not provide any easy way to do that. 

Following is what I implemented - though this is not a clean solution, but it works. 

  • Create a long text field. 
  • Create a field update and have the following formula
  • Include this field update in the workflow rule
                        IF ( INCLUDES ( Business_Rule__r.Suffix__c , "SFO" ), "SFO; ",null )&
                        IF ( INCLUDES ( Business_Rule__r. Suffix__c  , "LA" ), "LA; ",null )&

                        IF ( INCLUDES ( Business_Rule__r. Suffix__c  , "NY" ), "NY; ",null )&

                        IF ( INCLUDES ( Business_Rule__r. Suffix__c  , "DC" ), "DC; ",null )&
                        IF ( INCLUDES ( Business_Rule__r. Suffix__c  , "RIC" ), "RIC; ",null )

Suffix__c is a multi select picklist in the business rule object. 
Authentic Blog, featured by BlogUpp

Thursday, April 5, 2012

Cloud computing basics

History

The underlying concept of cloud computing dates back to the 1960s, when John McCarthy opined that "computation may someday be organised as a public utility."

The idea was simple, but very powerful. 
Amazon like many other companies realized that their data center was being utilized way less than their normal capacity. To be precise only 10%. So imagine, some one buys a server for some thousands of dollars, but only utilizes 10% of it, multiple this by the number of servers an organisation buys. 

Once the cloud computing architecture started getting better, Amazon came out with Amazon Web Services (AWS)  in 2006. This allowed the external customers to utilize the amazon infra structure on a utility basis. Which means you pay for only the amount of computing power, storage you use. 

And as they - Rest is History 

What is Cloud Computing

Enough of history, let's get to the point - 

Cloud computing is delivering a computing service to the end user, as opposed to a product over the internet. The user may or may not pay for the service. 

For example - Google documents, you can use this service for free over the internet, without installing any software. Google docs let's you create word documents, spread sheets and stores it for you. You don't have to physically store the files on your computer. If you have internet you can just login to your google account and view/edit your documents. 

There are numerous advantages of cloud computing, some of them are

1. No start up costs. 
2. No installation. 
3. No maintenance. 
4. Accessible anywhere any time provided you have access to internet. 
5. Pay per use.

Service Models

Cloud computing models vary: Infrastructure as a Service (IaaS), Platform as a Service (PaaS), and Software as a Service (SaaS). Manage your cloud computing service level via the surrounding management layer.
  • Infrastructure as a Service (IaaS). The IaaS layer offers storage and compute resources that developers and IT organizations can use to deliver business solutions. Amazon web service enables IaaS.
  • Platform as a Service (PaaS). The PaaS layer offers black-box services with which developers can build applications on top of the compute infrastructure. This might include developer tools that are offered as a service to build services, or data access and database services, or billing services. Google  provides both Paas and SaaS
  • Software as a Service (SaaS). In the SaaS layer, the service provider hosts the software so you don’t need to install it, manage it, or buy hardware for it. All you have to do is connect and use it. SaaS Examples include customer relationship management as a service. Salesforce.com is a major player in cloud CRM

Cloud Computing growth projections and predictions

There was a time when the concept was just a hype. Not many people/organizations were comfortable about the fact that their data will reside somewhere else and not in their own server  rooms. However, in the recent times, companies like Amazon, Google, Salesforce.com have been able to convince organizations about the strong data security policies in place. 

Cloud computing is now becoming a mainstream adoption. The encouraging fact is that a lot of banks, insurance companies are using cloud services to effectively reach their customers and to bring their operational costs down. 

According to a Gartner report global spending on SaaS (software as a service) will rise 17.9 percent this year to $14.5 billion. 

The recent purchases by major on premise companies like Oracle (bought Taleo for $1.9 billion)  and SAP (bought SuccessFactors for $3.4 billion) is a testimony to the fact that people are investing heavily into cloud technologies and the consumers are becoming increasingly comfortable to implement and use these technologies. 

A recent announcement from federal government is very encouraging for cloud based service providers and people working in this industry 

"The federal government has adopted a “cloud-first” policy that makes cloud, or Web-based computing, the default choice and has required agencies to move at least three services to the cloud within an 18-month period. "
Below are some of the projections according to industry reports 
  1. “Amazon Web Services [will] exceed $1 billion in cloud services business in 2012 with Google’s Enterprise business to follow within 18 months” (IDC).
  2. “[In 2012,] 80% of new commercial enterprise apps will be deployed on cloud platforms” (IDC).
  3. “At year-end 2016, more than 50 percent of Global 1000 companies will have stored customer-sensitive data in the public cloud” (Gartner)
Significant players in cloud computing
1. Amazon web services (IaaS)
2. Google (PaaS, SaaS)
3. Salesforce.com (PaaS, SaaS)
4. Workday (SaaS)
5.Microsoft Azure Service Platform (PaaS)
6. NetSuite (SaaS)
7. Rackspace (IaaS)

Linked in Groups

Some of the groups which can help you understand current happenings in the cloud and can help you network with these professionals are 

1. Cloud computing
2. Cloud Computing Marketing, Sales and Business Development
3. Cloud Computing, SaaS & Virtualization

Salaries

Make hay while the sun shines, this old saying cannot be more true. The industry is red hot, and there are numerous requirements for professionals with experience. Companies are willing to pay salaries over $100,000 for professionals with 4-5 years of cloud computing experience. Professionals with about 2 years of experience can earn anywhere from $65,000 to $90,000. 
Authentic Blog, featured by BlogUpp