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
r_boyd_848r_boyd_848 

HOW TO: PDF Attachments using Standard Controllers

I wanted to Email a PDF version of an Invoice page (PrintInvoice) I had created so I went about doing the usual stuff by creating a VF page that used the just the StandardController for a Custom Object called Invoice__c with the attribute renderAs="PDF"

 

Like so

 

 

<apex:page Standardcontroller="Invoice__c"  showheader="false"  renderAs="pdf">

<!-- all the rest of the page. THIS IS A SIMPLIFIED VERSION -->
<apex:pageBlockSection columns="1">
                <apex:pageBlockSectionItem >
                    <apex:outputText style="font-weight: bold;margin-right: 5px; font-size: medium;"
                        value="{!$Organization.Name}" />
                </apex:pageBlockSectionItem>
</apex:pageBlockSection>

<apex:pageBlockSection title="Charges for Patient: {!Invoice__c.patient__r.Patient_Name__c}  ({!Invoice__c.patient__r.ChartID__c})">
        <apex:dataTable headerClass="invLineHdr" columnClasses="invLineCentre" value="{!Invoice__c.Invoice_Line_Items__r}" var="li">
<!-- Columns and values -->

</apex:dataTable>
            
        </apex:pageBlockSection>

</apex:page>

 This all worked fine, I could preview the page from another page using an iFrame and Print the page using <apex:outputLink value="{!$Page.PrintInvoice}?id={!Invoice__c.id}" target="_blank">Print Invoice</apex:outputLink>

 

All good. However note that the page only uses the Standard Controller. I then wrote a class to email the invoice as a PDF attachment using the examples specified in Salesforce

 

 

 

public pageReference emailInvoice(){
		
		Messaging.Singleemailmessage mail = new Messaging.Singleemailmessage();
		PageReference pdf = Page.PrintInvoice;
		
		//prep attachment	
     		pdf.getParameters().put('id', invoiceId);
     		//pdf.setRedirect(true); //does not seem to be required
     		
     		Blob b = pdf.getContent();
     	
     		Messaging.EmailFileAttachment efa = new Messaging.EmailFileAttachment();
     		
     		efa.setFileName('invoice.pdf');
     		efa.setBody(b);
			
		string[] toAddr = new string[] {patient.Email__c};
		mail.setToAddresses(toAddr);
			
		mail.setSubject('Your Invoice from ' + UserInfo.getOrganizationName() + ' : Invoice No  ' + invoice.Name);
			
		mail.setHtmlBody('Thanks for attending:<b> ' + UserInfo.getOrganizationName() +' </b><p>'+
     			' Your Invoice is attached.');
 
     		mail.setFileAttachments(new Messaging.Emailfileattachment[] {efa});	
     		
     		Messaging.SendEmailResult [] r = 
			Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail});  
     		
     		addInfo('Email Send result: '+ r[0].isSuccess());
			
		
		return null;
	}

 

 

When I ran the code I started getting errors along the following lines

 

SObject row was retrieved via SOQL without querying the requested field: Invoice__c.Account__r

 

On further testing I couldn't even access fields on the main Invoice__c object. It seems that the getContent() call doesn't quite do it in terms of getting the object field values. (By the way . setRedirect() has no impact, you'll notice I commented it out). So far I didn't have to write any code to get the PrintInvoice page to display and as I was using the cool new FieldSets ability, I really didn't want to have go and write a entire custom controller to create a page that would work as a PDF attachment.

 

Extension Controllers to the rescue!!! Poking around the help files I noticed that if you call the getRecord() method on a Standard controller it will return all the fields specifed on the associated Visualforce page. In other words getRecord() should get all the fields from Invoice__c and any related object field names I've specified on the page.

 

So the solution was to create a controller extension:

 

 

public with sharing class InvoicePrintControllerExtension {

	private final Invoice__c invoice;
	
	
	public InvoicePrintControllerExtension(ApexPages.StandardController controller){
		
		
		this.invoice = (Invoice__c)controller.getRecord();
		
		
	}

}

 and the modify the apex page

 

 

<apex:page Standardcontroller="Invoice__c" extensions="InvoicePrintControllerExtension"  showheader="false"  renderAs="pdf">

 

 

This seems to force Apex to call the query to populate the VF page. It seems the getContent() by itself doesn't do enough to get the fields to populate on the page. The extension controller gets around this and so my Invoice feature happily fires off an email and I only had to write a couple of lines of code. Happy days!

 

Hope this helps someone

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Best Answer chosen by Admin (Salesforce Developers) 
r_boyd_848r_boyd_848

Another little bit of info, concerning test methods. It seems geContent is flakey when running testMethods so:

 

 

 

blob b;
     	
 try{
     b = pdf.getContent();
    }
     catch (System.Visualforceexception e) {
      //known issue when running testMethods against getContent so ignore, but add some data
      b = blob.valueOf('data');
     }

 

 

 

All Answers

r_boyd_848r_boyd_848

Another little bit of info, concerning test methods. It seems geContent is flakey when running testMethods so:

 

 

 

blob b;
     	
 try{
     b = pdf.getContent();
    }
     catch (System.Visualforceexception e) {
      //known issue when running testMethods against getContent so ignore, but add some data
      b = blob.valueOf('data');
     }

 

 

 

This was selected as the best answer
goabhigogoabhigo

So now does the PDF saved?

If yes, where?

rich-bbrich-bb

Just want to let you know that 2 years later this is still a relevant solution that helped me. Thanks!

toyed maryatoyed marya
I'm really happy I was can to improve insights from this great  Kahoot Login (https://kahoot-7.jimdosite.com/" target="_blank)