function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
thunksalotthunksalot 

How to turn a PDF visualforce page into a PDF attachment on a visualforce email template?

SHORT PROBLEM STATEMENT:  If someone has good guidance for the best way to go about splitting the below pdf-rendered visualforce page into a component(s) that will render identically when included in an Apex Page AND when pulled into a VF email template, I would be most grateful!  Obviously, my goal is to use (as much as possible) the same VF code and Apex controller code for the PDF both when it is rendered as an email attachment and when it is rendered from a VF page in a browser.

 

MORE DETAILS:

I created a Visualforce page (pasted below) to produce PDF invoices for our Opportunities.  I created a custom link to it so our staff can generate a PDF invoice for any given Opportunity with a click of their mouse.  It works beautifully.  Then, I wanted to attach the VisualForce page to VisualForce email templates we use for automating our renewal invoicing.  From the documentation, it appears that I have to convert it from a Page it into a Component in order to do that.  So, I tried turning it into a component that can be reference both by the Visualforce page that my custom link calls AND that can be pulled into my Visualforce email templates within <messaging:attachment> tag.  Sounds simple, right? 

 

Unfortunately, I'm pulling my hair out right now because my VF page uses the standard Opportunity controller, which I extended - whereas components cannot use standard controllers at all!  Does that mean I have to write a controller now that mimics everything that my VF page gets from the Opportunity controller?  Is there any easy way to create a controller that mimics everything a standard controller does? 

 

Obviously, my goal is to use (as much as possible) the same VF code and Apex controller code for both the PDF rendered as an email attachment and the PDF when rendered from a browser.  If someone has general guidance for the best way to go about splitting the below pdf-rendered visualforce page into a component(s) that will render identically when included in an Apex Page AND when pulled into a VF email template, I would be most grateful!  

 

BTW - you may notice that my styles include data from the controller.  That's because putting data in a PDF header or footer requires putting it in the "content" attributes of the @page styles.  This has proven to be a problem in my experiments with componentizing this page.  Advice on how to handle the styles, given that they need access to Opportunity data, would be a big help, too.

 

THANK YOU ALL IN ADVANCE!!!!!!!

 

<apex:page standardController="Opportunity" showHeader="FALSE" standardStylesheets="false" renderas="pdf">
<head>
<style type="text/css">
@page{
    size: letter;
    margin: 10%;
    @top-center {
        content: "Invoice Date: {!MONTH(TODAY())}/{!DAY(TODAY())}/{!YEAR(TODAY())}";
            font-family: Helvetica, Arial, sans-serif;
            font-size: 12px;
            font-weight: bolder;
    }
    @top-right {
        content: "Invoice #: {!opportunity.ID_for_invoices__c}";
        font-family: Helvetica, Arial, sans-serif;
        font-size: 12px;
        font-weight: bolder;
    }
    @top-left {
        content: "INVOICE";
        font-family: Helvetica, Arial, sans-serif;
        font-size: 12px;
        font-weight: bolder;
    }
    @bottom-left {
        content: "";
        font-family: Helvetica, Arial, sans-serif;
        font-size: 10px;
    }
    @bottom-right {
        content: "Page " counter(page) " of " counter(pages);
        font-family: Helvetica, Arial, sans-serif;
        font-size: 10px;
    }
}
body {
    font-family: Helvetica, Arial, sans-serif;
    font-size: 11px;
}
table {
    font-family: Helvetica, Arial, sans-serif;  
}
td {
    border: 1px solid #000000;  
}
.tablelabel {
    background: #000000;
    color: #FFFFFF;
    padding: 5px;
    font-weight: bolder;
}
.tableheader {
    font-weight: bolder;
}
.invisiblecell {
    border-left: 0;
    border-bottom: 0;
    border-top: 0;
}
span#warningtext{
    font-size: 80%;
    background: #FFF000;
    float: left;
    padding: 3px;
    margin-right: 8px;
    width: 90px;
    align: middle;
    text-align: right;
}

</style>
</head>

<p><strong>Billed To:</strong></p>
<p>{!opportunity.Account.Name}<br/>
<apex:outputText value="{! IF(ISBLANK(opportunity.Account.BillingStreet),"", opportunity.Account.BillingStreet &"<br/>")}" escape="false"/>
{!opportunity.Account.BillingCity}, {!opportunity.Account.BillingState} {!opportunity.Account.BillingPostalCode}<br/>
{!opportunity.Account.BillingCountry}
</p>

<!-- OUTPUT INVOICE COMMENT, IF ANY  -->
<apex:outputpanel rendered="{! NOT(ISBLANK(opportunity.invoice_comment__c))}">
<p><strong>Comment:</strong> {!opportunity.Invoice_Comment__c}</p>
</apex:outputpanel>

<!-- OUTPUT PROMOTION DESCRIPTION, IF PROMOTION APPLIED -->
<apex:outputpanel rendered="{! NOT(ISBLANK(opportunity.promotion_code__c))}">
<p><strong>Promotion:</strong> {!opportunity.Promotion_Code__c}</p>
</apex:outputpanel>

<!-- OUTPUT ORDERED ITEMS TABLE -->
<table class="tables" cellpadding="6" cellspacing="0" width="100%">
<tr><td colspan="5" class="tablelabel">Order</td></tr>
<tr class="tableheader"><td>Item Description</td><td align="right">List Price</td><td align="right">Sale Price</td><td align="center">Quantity</td><td align="right">Amount</td></tr>
<tr>
</tr>

<apex:repeat var="lineitem" value="{!opportunity.OpportunityLineItems}">
<tr>
<td align="left" width="50%">{!lineitem.PricebookEntry.Product2.Name}</td>
<td align="right"><apex:outputfield value="{!lineitem.ListPrice}" /></td>
<td align="right"><apex:outputfield value="{!lineitem.UnitPrice}" /></td>
<td align="center">{!lineitem.Quantity}</td>
<td align="right"><apex:outputfield value="{!lineitem.TotalPrice}" /></td>
</tr>
</apex:repeat>
<tr><td class="invisiblecell"></td><td colspan="3" class="tablelabel">Order Total</td><td class="tablelabel" align="right"><apex:outputfield value="{!opportunity.Amount}" /></td></tr>
</table>

<p>&nbsp;</p>
<p><strong>To Pay by Credit Card</strong><br/>
We are available to take credit card payments over the phone between 8am and 5pm ET at XXX-XXX-XXXX.</p>

<p>
<strong>To Pay by Check</strong> <br/>
Make checks payable to “XXXXX”, include a memo that the amount is for "XXXXX" and mail to:</p>


<p><span id="warningtext">Please note our<br />new address<br />for payments</span>AASHE<br />
PO Box XXXXXXX<br />
Philadelphia, PA XXXXXX 
</p>

<p><strong><em>Thank you!</em></strong>
</p>

</apex:page>

 

thunksalotthunksalot

still looking for help on this one.  Any ideas?

thunksalotthunksalot

still looking for help on this one.  Please be my savior.

thunksalotthunksalot

Oh man, this is bad.  It occured to me to see how Salesforce Labs has solved this problem, given that their Simple Quote/Invoice/Order outputs identical invoices as an email attachment and as a PDF visualforce page generated by a button.  Remember, my problem is that I want to reuse my VF and controller code to produce the same invoice in those two ways - email attachment and as a PDF page available for one-click download - but ran into the limitation that if I componentize the invoice code so that it can be used in both places, I can't used the standard Opportunity controller (because custom components don't allow the use of a standard controller).   So, how did SF's own developers get around this problem?!  They duplicated 100% of the invoice code in both places!  No code reuse at all.  In their implementation the invoice code has to be maintained in two independent places.  How lame!

 

If we did it that way, we'd have to duplicate the invoice code dozens of times (once for every email template we have that want to attach an invoice to).  So, this is bad because it's making me think there just isn't any way to do this without duplicating a lot of code.  I either have to duplicate the functionality of the standard Opportunity controller in a custom controller that can be used by custom components - or, I have to duplicate my invoice code everywhere.  Right?

Lance Havens 4Lance Havens 4
Did you ever resolve this? Looking to do the same thing. 
Jaco de LangeJaco de Lange
Need to do the same here ?