Stat Tracker

Sunday, February 26, 2012

Generating a PDF in an Apex Trigger

There are a number of ways you can generate PDF documents on Force.com. The easiest way is to simply use the 'RenderAs' attribute on a Visualforce Page. You can also generate quote PDF easily with templates out of the box. While these options are great and work in a number of situations, they will not work within the context of a trigger.

There are many times during the life of a record where we may need to automatically generate a PDF document and attach it to the record. To do this we can use the Blob object and its method toPDF(String content). I was actually surprised by how little information there is available on this method. There are lots of blogs and articles about rendering a Visualforce page as a PDF, but this method hardly gets any press!

The Blob.toPDF() method takes in the body of the PDF document as a String. And guess what! It can take in HTML content and render the PDF according to the HTML you specify. Pretty awesome! Let's take a look at this in action. I've created a simple Apex Trigger on my Account object which will generate a simple PDF of all the information on the Account object.

Here's the Account Trigger:

trigger AccountTrigger on Account (after delete, after insert, after undelete,
after update, before delete, before insert, before update)
{
    if(trigger.isAfter)
    {
        if(trigger.isInsert)
        {
            if(trigger.new.size() == 1) // Lets only do this if trigger size is 1
            {
                AccountPDFGenerator.generateAccountPDF(trigger.new[0]);
            }
        }else if(trigger.isUpdate)
        {
            if(trigger.new.size() == 1) // Lets only do this if trigger size is 1
            {
                AccountPDFGenerator.generateAccountPDF(trigger.new[0]);
            }
        }
    }
}


And here is the PDF Apex class which will use the Blob.toPDF() method to generate the PDF content and attach it to the Account record as an Attachment. Notice that ths Blob.toPDF() takes in a String as the input, and that the String content can be formatted with HTML.

public with sharing class AccountPDFGenerator
{
   
    public static final String FORM_HTML_START = '<HTML><BODY>';
    public static final String FORM_HTML_END = '</BODY></HTML>';

    public static void generateAccountPDF(Account account)
    {
        String pdfContent = '' + FORM_HTML_START;
        try
        {
            pdfContent = '' + FORM_HTML_START;
            pdfContent = pdfContent + '<H2>Account Information in PDF</H2>';
           
            //Dynamically grab all the fields to store in the PDF
            Map<String, Schema.SObjectType> sobjectSchemaMap = Schema.getGlobalDescribe();
            Schema.DescribeSObjectResult objDescribe = sobjectSchemaMap.get('Account').getDescribe();
            Map<String, Schema.SObjectField> fieldMap = objDescribe.fields.getMap();
           
            //Append each Field to the PDF
            for(Schema.SObjectField fieldDef : fieldMap.values())
            {
                Schema.Describefieldresult fieldDescResult = fieldDef.getDescribe();
                String name = fieldDescResult.getName();
                pdfContent = pdfContent + '<P>' + name + ': ' + account.get(name) + '</P>';
            }
            pdfContent = pdfContent + FORM_HTML_END;
        }catch(Exception e)
        {
            pdfContent = '' + FORM_HTML_START;
            pdfContent = pdfContent + '<P>THERE WAS AN ERROR GENERATING PDF: ' + e.getMessage() + '</P>';
            pdfContent = pdfContent + FORM_HTML_END;
        }
        attachPDF(account,pdfContent);
    }
   
    public static void attachPDF(Account account, String pdfContent)
    {
        try
        {
            Attachment attachmentPDF = new Attachment();
            attachmentPDF.parentId = account.Id;
            attachmentPDF.Name = account.Name + '.pdf';
            attachmentPDF.body = Blob.toPDF(pdfContent); //This creates the PDF content
            insert attachmentPDF;
        }catch(Exception e)
        {
            account.addError(e.getMessage());
        }
    }
   
}


And here is the PDF that was generated in the Apex Trigger and attached to the Account record.


Pretty awesome, right? Since you can't get a Visualforce page's content in a Apex Trigger this method is a lifesaver when you need to automatically generate simple PDF documents via Triggers.

Let me know your thoughts and if you have any other feedback on this method!

Tuesday, February 7, 2012

Fun Times with the Force.com Metadata API

So this past weekend I spent a few hours playing around with the Force.com Metadata API. Currently Force.com is running a developer contest to drive folks to do some cool stuff with it. In my everyday Force.com adventures, this is not a feature I get to play with alot. Typically I use the Force.com IDE to do small deploys, or the Force.com Migration Toolkit to run more complex deployments (both of which use this API under the hood).

However, there are some cool things you can do with the API. You can retrieve and update all your objects, apex code, triggers, visualforce, static resources, etc via the API. Meaning you can build tools that make deployments easy. You could even build an application to take snapshots of all your Metadata on a Nightly Basis for backing up and creating versions of objects. There are all sorts of cool stuff you can do!

For example, my simple java application reads a local MySQL database schema information and uploads it to Force.com. Here's a screenshot of the beta or work in progress if you will:

You can also take a look at this demo on YouTube here: http://www.youtube.com/watch?v=OTQcayubCEA&context=C3378280ADOEgsToPDskKXJUZxact1OiFZosSoQ_52

Hopefully I'll get this sucker finished up soon. Once I do I'll post the code to GitHub along with a follow up article about it about the technical details. But if you're reading this, your probably interested in the API and what you can do. And really the sky's the limit!

I'd like to also give a shout out to Cloud Converter from Model Metrics while I'm talking about this. This application has been around a while and is available on the App Exchange, and its great for building data dictionaries for clients. Its Open Source too, so you can take a peak of the code on Google Code. http://developer.force.com/projectpage?id=a0630000003LD3HAAW