Visualforce EmailQuote2PDF


Image:Header_sample.gif

Visualforce Sample - PDF Quotes and Email Templates

A new feature, recently introduced with the Winter '09 release, allows Visualforce markup to be used in the definition of an Email Template. As an additional sweetener, the PDF Conversion facilities supported in Visualforce pages have been extended to email templates for simple generation of dynamic attachments delivered as PDF documents.

What better way to illustrate this capability than to extend the Visualforce Pages2PDF sample that was shared for the Summer '08 release?

Image:visualforce_EmailQuote2PDF_screenshot.png

Getting this sample

As mentioned in the introduction above, this sample extends the general Quote2PDF sample that was published last summer. In addition to Visualforce email template, the winter '09 release brought with it the ability to package Visualforce Pages, Components, and Static Resources and as such the schema-only package from the previous sample has been updated with all the pages, resources, apex classes and even the email template for easy consumption by installing this updated version of the package.

What's new here?

Developing Visualforce Email Templates is very similar to developing a Visualforce Page. To understand the similarities and differences, the email template (complete version below) will be compared to the QuotePDF page from the previous sample.

The container tag

It starts with the first line of each, first the page:

<apex:page standardController="Quote__c" showHeader="false" renderAs="pdf">

And now the email template:

<messaging:emailTemplate subject="Your requested quote #{!relatedTo.Name}" recipientType="Contact" relatedToType="Quote__c">

The first thing you'll notice is the different container tag. The emailTemplate component in the messaging namespace signifies this will generate an email rather than a page.

Second, if you are familiar with Visualforce pages you might be looking for the controller type on the messaging:emailTemplate tag. The controller for an email template is a composition of the standard controllers you'd find for a page bound to the respective recipientType and relatedToType, e.g. Contact and Quote__c. In other words, for Visualforce email templates there are always 2 root objects from which you can bring in information.

The page in the previous example was rendered as PDF but in the email we'll use PDF on the attachment as you'll see below.

Finally, the email has a subject which is defined through the attribute with the same name on the messaging:emailTemplate tag.

Special tags

In addition to the container there are additional components that are specific to Visualforce email templates:

  • messaging:attachment
  • messaging:emailHeader
  • messaging:htmlEmailBody
  • messaging:plainTextEmailBody

See the Component Reference Appendix in the Visualforce Developer Guide for additional information.

Only two are utilized in the sample provided below: messaging:attachment and messaging:plainTextEmailBody.

messaging:attachment includes the PDF conversion facility. The body of this tag in the example below is essentially treated like the quotePDF page in the previous example. Specifically the markup is converted to PDF per the specification of the renderAs attribute and in this case attached to the email:

<messaging:attachment renderAs="pdf" filename="{!relatedTo.name}">

The value of the filename attribute will automatically get appended with the pdf extension (see screenshot above).

Data binding

The differences in data binding are fairly straight-forward. To see the difference, we'll compare the expression to bring in the quote name (number).

In the page the reference is:

value="Quote# {!Quote__c.name}"

While in the email template the reference is:

	
value="Quote# {!relatedTo.name}"

In each case above the object referenced by the expression is a Quote__c custom object instance so all field and relationship references from relatedTo and Quote__c , e.g. name, will resolve equally in the respective contexts.

When the PDF is generated in the UI the contact is always known through the relationship on the quote and therefore the recipient is tightly bound to the quote itself:

<apex:outputText value="{!Quote__c.contact__r.name}" styleClass="contactName"/>

When composing an email the user can specify any recipient of the quote and assuming the quote needs to reflect that the recipient specified by the email action might be used instead:

<apex:outputText value="{!recipient.name}" styleClass="contactName"/>

Email Template (complete)

<messaging:emailTemplate subject="Your requested quote #{!relatedTo.Name}" recipientType="Contact" relatedToType="Quote__c">
    <messaging:plainTextEmailBody >
        Dear {!recipient.name},
        
        Thank you for your continued interest in our offering. Please see the attached quote per your request.
        
        Feel free to contact me if you have any questions.
        
        Regards,
        {!$User.firstname} {!$User.lastname}
    </messaging:plainTextEmailBody>
    <messaging:attachment renderAs="pdf" filename="{!relatedTo.name}">
        <apex:stylesheet value="{!URLFOR($Resource.pdfresource, 'styles.css')}"/>
        <style>
            body { padding:5px; }    
            .companyName { font-weight:bold; font-size:16px; }           
            .customerName { text-transform:uppercase; font-size:14px; } 
            .contactName { font-weight:bold; font-size:14px; }
            .line { background-image: url({!URLFOR($Resource.pdfresource, 's.gif')}); background-color:black; height:4px; margin:10px 3px; }
            .lineSmall { background-image: url({!URLFOR($Resource.pdfresource, 's.gif')});  background-color:black; height:2px; margin:10px 3px; }            
            .companyTable { padding-top:15px; }            
            .left { text-align:left; vertical-align:top; }
            .right { text-align:right; vertical-align:top; }
            .total { width:20%; font-weight:bold; font-size:12px; }            
            .totalLabel { width:80%; font-weight:bold;font-size:12px;}            
            .productName { font-weight:bold; font-size:14px; }        
            .productDetail { margin-left:10px; }
        </style>
        <apex:image value="{!$Resource.templates_logo}"/>
        <apex:panelGrid columns="1" styleClass="companyTable" width="100%">
            <apex:outputText value="{!$Organization.Name}" styleClass="companyName"/>
            <apex:outputText value="{!$Organization.Street}"/>
            <apex:outputText value="{!$Organization.City}, {!$Organization.State} {!$Organization.PostalCode}"/>
            <apex:outputText value="{!$Organization.Phone}"/>
        </apex:panelGrid>
        <apex:outputPanel layout="block" styleClass="line"/>
        <apex:panelGrid columns="1" styleClass="centered" width="100%">
            <apex:panelGrid columns="2" width="100%" cellpadding="0" cellspacing="0" columnClasses="left,right">
                <apex:outputText value="Quote# {!relatedTo.name}"  styleClass="customerName"/>
                <apex:outputField value="{!relatedTo.lastmodifieddate}" style="text-align:right"/>
            </apex:panelGrid>
            <apex:outputText value="{!relatedTo.opportunity__r.account.name}" styleClass="customerName"/>
            <apex:outputText value="{!recipient.name}" styleClass="contactName"/>
        </apex:panelGrid>
        <apex:panelGrid columns="1">
            <apex:outputText value="{!relatedTo.opportunity__r.account.name}"/>
            <apex:outputText value="{!recipient.mailingStreet}"/>
            <apex:panelGroup >
                <apex:outputText value="{!recipient.mailingCity}"/>
                <apex:outputText value=", {!recipient.mailingState}"/>
                <apex:outputText value=" {!recipient.mailingPostalCode}"/>
            </apex:panelGroup>
            <apex:outputText value="Phone: {!recipient.phone}"/>
        </apex:panelGrid>
        <apex:outputPanel layout="block" styleClass="lineSmall"/>
        <apex:repeat value="{!relatedTo.quote_items__r}" var="line">
            <apex:panelGrid columns="2" columnClasses="left,right" width="100%">
                <apex:panelGroup >
                    <apex:outputText value="{!line.name}" styleClass="productName"/>
                    <apex:outputPanel layout="block" styleClass="productDetail">
                        <apex:panelGrid columns="2" columnClasses="left,none">
                            <apex:outputText value="Units:" style="font-weight:bold"/>
                            <apex:outputField value="{!line.Quantity__c}"/>
                            <apex:outputText value="Unit Price:" style="font-weight:bold"/>
                            <apex:outputField value="{!line.Unit_Price__c}"/>
                            <apex:outputText value="Product Code:" style="font-weight:bold"/>
                            <apex:outputField value="{!line.product__r.productCode}"/>
                            <apex:outputText value="Description:" style="font-weight:bold"/>
                            <apex:outputField value="{!line.product__r.description}"/>
                        </apex:panelGrid>
                    </apex:outputPanel>
                </apex:panelGroup>
                <apex:outputField value="{!line.Total_Price__c}" styleClass="productName"/>
            </apex:panelGrid>
        </apex:repeat>
        <apex:outputPanel layout="block" styleClass="lineSmall"/>
        <apex:panelGrid columns="2" columnClasses="right" width="100%">
            <apex:panelGrid columns="2" cellpadding="10" columnClasses="right totalLabel,right total" width="100%">
                <apex:outputText value="Total"/>
                <apex:outputField value="{!relatedTo.Total_Price__c}"/>
            </apex:panelGrid>
        </apex:panelGrid>
        <apex:outputPanel layout="block" styleClass="line"/>
    </messaging:attachment>
</messaging:emailTemplate>

Installing this sample

In order to consume this sample within your developer edition or sandbox account you should follow these steps:

  1. Install this package
  2. Add the Quote related list to your Opportunity page layout
  3. Replace the standard "New" button with the custom "New Quote" button in the Quote related list

Using this sample

Once you have the package installed and deployed and the additional configurations as described above follow these steps to see how this works:

  1. Navigate to an opportunity in your account. Make sure there is at least one product and a primary contact role on your opportunity.
  2. Scroll down to the Quote related list and click on the custom "New Quote" button.
  3. Complete the form, and click "Save". You should now be on the quote page.
  4. Scroll down to the standard Activity History related list and click on the "Send an Email" button.
  5. Choose a recipient.
  6. Click on the "Select Template" button and choose the new template named "Quote".
  7. Wait for the page to be updated with the template merged through with values from your record.
  8. Scroll (if necessary) to the bottom of the page to the "Attachments" related list. Notice the PDF file.
  9. Click on the file name and you should be prompted to download your dynamically created PDF

If the recipient you chose has your email address on it you can go ahead and click send. If not Visualforce Email Templates provide a very useful testing facility under setup. To check that out:

  1. Navigate to Setup > Administration Setup > Communication Templates > Email Templates
  2. Select the Visualforce Quote2PDF folder that came with the package you installed
  3. Click on the link to the Quote email template (Email Template Name column)
  4. Click on "Preview".

You should now see something like the following, allowing you to generate test emails using real data and optionally send it to yourself!

Additional Resources