[SalesForce] How to access a VF page as PDF within trigger/scheduled APEX

I have run into yet another problem due to restrictions placed by Salesforce.

So apparently we cannot make a call to a Visualforce page using getContent() from within a Apex trigger.

Currently we have an apex trigger that is executed when some new records are created and it sends emails to the portal users to let them know that new records are on the portal.

Now we also need to send pdf attachments to these users having the same look and feel as the portal page. My VF page were already designed to be rendered as pdfs so I figured I could make a getContent() call and store the reponse as a blob and attach it to the email object but it looks I can't do that.

Next I tried doing this from within the trigger.

                    HttpRequest request = new HttpRequest();
                    request.SetMethod('POST');
                    request.SetEndPoint('my URL');

                    HttpResponse httpResponse = new Http().Send(request);
                    body = httpResponse.GetBodyAsBlob();

Apparently even this does not work as callouts are not allowed from triggers!

So I read up on Scheduled APEX and wrote a class that implements Schedulable.
getContent() does not work in scheduled APEX either so I now try to see if callouts work.

I created a method in this class with the @future annotation. I noticed that the httpResponse.GetBodyAsBlob(); was returning null. I verified that my URL was correct by directly accessing it in browser(Page showed up fine).

I then changed the method to POST. This time there is some data returned and my attachments were created. But the data seems to be messed up as adobe refuses to open these files plus their size seems to suggest something went wrong(<1kb).

This is how I am setting the content type to pdf.

                  Messaging.EmailFileAttachment attach = new Messaging.EmailFileAttachment();
                    attach.setContentType('application/pdf');
                    attach.setFileName(inv.Name+'.pdf');
                    attach.setInline(false);
                    attach.Body = body;
                    emailAttachList.add(attach);

So what can I do here? I then read this https://stackoverflow.com/questions/9404751/why-are-html-emails-being-sent-by-a-apex-schedulable-class-being-delivered-with

But I have more or less done the same thing…What am I doing wrong? Can someone please suggest a solution or workaround so I can access my VF page within an apex trigger or class and get the content to send it as a pdf attachment.

EDIT
When I look at my debug logs I see that my GET request returns a 302 which must mean that its redirecting to the Salesforce login page. How do I modify my GEt request witjh authentication to the VF page?

                HttpRequest request = new HttpRequest();
                request.SetMethod('GET');
                request.SetEndPoint('my URL');

                HttpResponse httpResponse = new Http().Send(request);
                body = httpResponse.GetBodyAsBlob();

Best Answer

For a low/no code solution, you could play around with something like this:

Step 1: Create a Picklist on your custom object (lets call this Template_c on object Invoice_c) which will be used to dictate the Email attachment content, e.g. Picklist values

  • Email Template 1
  • Email Template 2
  • Email Template 3

Step 2: Create your Email template as a Visualforce template, only rendering the relevant content for your attachment, for example:

<messaging:emailTemplate subject="Invoice - {!relatedTo.Name}" recipientType="Contact" relatedToType="Invoice__c">
    <messaging:attachment renderAs="pdf" filename="{!relatedTo.Name} rendered="{!relatedTo.Template__c == 'Email Template 1'}">  
        <c:MyVFForTemplate1/>    
    </messaging:attachment>
    <messaging:attachment renderAs="pdf" filename="{!relatedTo.Name} rendered="{!relatedTo.Template__c == 'Email Template 2'}">    
        <c:MyVFForTemplate2/>    
    </messaging:attachment>
    <messaging:attachment renderAs="pdf" filename="{!relatedTo.Name} rendered="{!relatedTo.Template__c == 'Email Template 3'}">    
        <c:MyVFForTemplate3/>    
    </messaging:attachment>
    <messaging:htmlEmailBody >
        Dear Customer,

        Here is your Invoice.  Please pay now, or else!
    </messaging:htmlEmailBody>
</messaging:emailTemplate>

(note, i have illustrated the example using a VF component but you could just as well copy/paste the VF page content into the Email template.

Step 3: Create a workflow rule with the following attributes:

  • Object = Invoice (Invoice__c)
  • Evaluate the rule when a record is: created, and any time it’s edited to subsequently meet criteria
  • Run this rule if the following criteria are met Template__c = 'Email Template 1' or 'Email Template 2' or 'Email Template 3'
  • 'Save & Next'

Step 4: Add a new Workflow Action: New Email Alert, selecting the Email template you just created

Step 5: Enable/Activate your workflow

Step 6: Update your Invoice record's Picklist value to one of the relevant picklist values and the workflow should fire (as a result of Step 3) sending an email (as a result of Step 4) in the format required (as a result of Step 2).

I haven't tested the above, as I've just typed it directly in here but this approach works as I have used it before.

Related Topic