I recently had to integrate our USA and our Switzerland subsidiaries in our quoting tool. This tool, so far, used a fairly hard-coded visualforce page with some static stylesheets to generate a DIN 5008 (Type A) letter quote. While our Swiss subsidiary only required an easy way to add standard texts, it turns out that our American subsidiary had very specific demands. This required me to re-think the whole architecture of my application. Yes, some of you might rest their faces in their palms now, but bear with me for a moment: I bet most Americans are not aware of it either, that their way of cutting paper is different from how the rest of the world does it. So it was time to lift customizability and configurability to the next level with Visualforce Dynamic Component(s) and some RegExp magic.

Requirements

Most of the requirements were fairly easy to implement: the paper format, different date formatting and individual rules how to display tax and shipping all were just a couple of lines of code. But one particular requirement got me thinking: the individual address formatting. As we know, formatting rules for addresses around the world are pretty different. So simply hard-coding the american address right next to the german address in the visualforce page was no option. What I needed was a flexible way for each subsidiary to configure the display format of the recipient address by themselves.

Business Requirements

To summarize, I had the following requirements from my businesses

  • Formattings configurable per subsidiary (or company profile)
    • Date formatting (based on the subsidiary, not the user who generates the PDF)
    • Letter/paper format
    • Recipient’s address formatting
    • Display options for the tax columns, as well as net and gross prices
  • Every subsidiary wants to manage its own standard texts that are attached to the quote (like terms of payment, shipping terms, additional information, etc)
  • Multi-language support for the complete content of the PDF. The language must be manually selectable and is not based on the user’s setting who generates the PDF.

Administrative Requirements

In addition to businesses requirements, I added to following maintainability and scalability requirements. This is to ensure, that the business does not require an administrator (the typical bottle neck in our organization) to perform changes on their templates:

  • All configuration (especially texts) must be maintainable by business users. Abstraction must be high enough, that no developer skills are required to „set up a PDF template“.
  • All configuration related to a subsidiary must be stored as records in custom sObjects. As a result, onboarding a new subsidiary is as easy as adding a couple of new records and configuration is accessible to business users.
  • Easy to implement new requirements as they come up. The core of the pdf template must be designed to be configurable and the configuration options must be easily extendable.

Solution Design

Since this blog post is not about the whole solution architecture of my quoting tool, I will not bore you with my take on Company Profiles, PDF Templates, Text Components and Component Translations. Instead, let’s take a look at the functionality to dynamically switch the paper and letter format, configure the date formatting and how to specify a custom address formatting by using a special markup language.

Dynamically Selecting the Paper Format CSS Using URLFOR

This turns out to be pretty easy: The CSS @page rule allows us to define the paper format to use, when the page is printed. A simple static CSS resource with one line of code does the trick:

/** DIN 5008 (Type A) with margins and paddings */
@page {
size: A4 portrait;
margin: 27mm 0mm 32mm 0mm; /** These margins control size of header and footer */
padding: 5mm 0mm; /** Paddings for content page area to header/footer */
}
/** ANSI Letter without individual margins */
@page {
size: letter portrait;
}

Now we just need an easy way to tell the controller, which CSS to use when the page is printed. A simple picklist field on the PdfTemplate__c configuration object. The picklist api name values match the exact names of the static resources, because we will use this values in the Visualforce page to dynamically reference the CSS.

<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
[...]
<valueSet>
<restricted>true</restricted>
<valueSetDefinition>
<sorted>false</sorted>
<value>
<fullName>DIN_5008_Type_A</fullName>
<default>true</default>
<label>DIN 5008 (Type A)</label>
</value>
<value>
<fullName>ANSI_Letter</fullName>
<default>false</default>
<label>ANSI Letter</label>
</value>
</valueSetDefinition>
</valueSet>
</CustomField>

In the controller, we expose the Template property that includes our LetterFormat__c picklist …

public static PdfTemplate__c Template {
public get {
if (Template == null && TemplateId != null) {
Template = [SELECT Id,AddressFormatting__c,DateFormatting__c,Description__c,IsActive__c,LetterFormat__c,TaxDisplayOptions__c FROM PdfTemplate__c WHERE Id = :TemplateId];
}
return Template;
}
private set;
}

… and use this property in the visualforce page to embed the stylesheet based on the picklist value with URLFOR:

<apex:variable rendered="{! Template.LetterFormat__c != NULL }" var="LetterFormat" value="{! Template.LetterFormat__c }">
<apex:stylesheet value="{! URLFOR($Resource[Template.LetterFormat__c]) }" />
</apex:variable>

Dynamically Render the Date Formatting Using a Controller Property

Similar to the letter format solution, I introduced custom text fieldDateFormatting__c to PdfTemplate__c . This is where our business users configure their desired date display format in the Java SimpleDateFormat (used by the visualforce apex:outputText component internally).

We construct the complete formatter string as a lazy-init property in the controller …

public static String DateFormatting {
public get {
if (DateFormatting == null) {
if (String.isNotBlank(Template.DateFormatting__c)) {
DateFormatting = '{0, date, '+ Template.DateFormatting__c +'}';
} else {
DateFormatting = '{0, date, dd.MM.yyyy}';
}
}
return DateFormatting;
}
private set;
}

… and use the property in the visualforce page to display any date with the configured formatting:

<apex:outputText value="{!DateFormatting}">
<apex:param value="{! Record.Your_Date_Field__c }" />
</apex:outputText>

Dynamically Render a Custom Address Format With a Dynamic Component

The definitely most interesting requirement was the address formatting, that our USA subsidiary wanted to employ. All our users will work with the BillingAddress and ShippingAddress standard fields, but each subsidiary might use its own format how these fields are arranged when a PDF is generated. Since I cannot know all possible world wide address patterns in advance, I designed a solution that enables the use of a basic markup syntax to define merge fields, raw text and line breaks.

Address Markup and Functionality

For example, our american subsidiary uses the following markup to display addresses in their american formatting …

{! BillingStreet }
{! BillingCity }, {! BillingStateCode } {! BillingPostalCode }

… which is rendered like this in the letter head:

Dynamic component in action, displaying an american address formatting

Our german head quarter uses the following markup, to style german addresses …

{! BillingStreet }
{! BillingPostalCode } {! BillingCity }
{! BillingCountryCode }

… which will render the exact same quote record like this:

Dynamic component in action, displaying a german address formatting

To summarize, this setup allows business users (delegated admins of our subsidiaries) to manage their address formattings all by themselves. They can use the following syntax, to define virtually any valid address formatting:

  • Enter new lines. Text and merge fields can be arranged on up to 5 lines.
  • Enter merge fields, similar to how they already know it from email templates using the syntax {! FieldApiName }. Custom fields and standard fields are equally supported and picklist field labels are even localized to the display language of the Visualforce page.
  • Enter any arbitrary text. Text can even be formatted in size, color, font type, etc (of course you have to test, if the text will still be displayed within the boundaries of the address box)

Implementing the Visualforce Dynamic Component

To be consistent with the other configuration options, I introduced a new Rich Text Field AddressFormatting__c. Here, users can configure their address formatter markup. Html text fields natively support line breaks (they are enclosed in <p> – tags), so I did not have to come up with a custom markup for this.

The markup string is stored as HTML formatted text, so the previous examples are stored in the database like this:

<p>{! BillingStreet }</p><p>{! BillingPostalCode } {! BillingCity }</p><p>{! BillingCountryCode }</p>
<p>{! BillingStreet }</p><p>{! BillingCity }, {! BillingStateCode } {! BillingPostalCode }</p>

The parser is implemented in its own class DynamicAddress.cls. New instances are constructed with the markup string as the only parameter of the constructor and can be re-used multiple times to generate a PageBlock that will be used for the dynamic component. The engine uses regular expressions to break up the markup string by its <p> tags in address lines and identify {! FieldApiName } syntax as merge fields. Essentially, it generates <apex:outputField> tags for merge fields and <apex:outputText> for line breaks and any raw texts.

To use the parser in any Visualforce controller, simply initialize it with the formatter pattern from the configuration record, bind it with the name of the record that is used throughout your Visualforce page (QuoteRecord in my example) and call getAddressAsPageBlock() to generate the dynamic page block that can be added as a Visualforce dynamic component.

public static Component.Apex.PageBlock DynamicAddress {
public get {
if (DynamicAddress == null && Template != null && String.isNotBlank(Template.AddressFormatting__c)) {
DynamicAddress dynAddr = new DynamicAddress(Template.AddressFormatting__c);
dynAddr.RecordVariableName = 'QuoteRecord';
DynamicAddress = dynAddr.getAddressAsPageBlock();
}
return DynamicAddress;
}
private set;
}
<apex:dynamicComponent componentValue="{! DynamicAddress }" />

Considerations

So why even bother with a Visualforce Dynamic Component and not simply generate the pre-formatted text with Apex and output it with <apex:outputText escape="false">? Well, turns out that Apex does not support localization of picklist values. SOQL always queries the records in the language of the user, even when using toLabel(Picklist_Field__c). Since my users dynamically select the language of the PDF, I must ensure, that all translated content is displayed in the language context of the visualforce page, not the user’s scope.

Example Code

Because of the complexity of the solution, I decided to create a demo repository with the implementation of the parser and a demo Visualforce page, that shows the use of an <apex:dynamicComponent> to dynamically display an address based on an customized markup string.

You can find the full demo implementation here: https://github.com/jlietzau/dynamic-address-demo

Closing Thoughs

One of the most interesting architectural decisions when designing a solution is the desired genericity of the program. Most products I know (appero quote as a very mature example) are pretty generic, so they require a lot of configuration and domain specific knowledge to setup. They also come with way more functionality than what the average customer needs, so setup and maintenance usually require a lot of training. On the other hand, there are highly specific and basically „hard-coded“ customizations, that are specifically tailored for one use case and this use case only.

I opted for an implementation that offers us a lot of flexibility and is inherently open for extension, while still encapsulating a lot of complexity. This makes it easy for our business to configure essential settings. Our layout is still pretty much fixated, but I could later add the option to dynamically upload your own CSS on the PDF Template to style the page. Onboarding a new subsidiary (this includes formatting the PDF options and defining all standard texts) takes us less than 5 minutes. And because these tasks are not too complex, you do not need a specifically trained admin to perform them.

What do you think? How generic should a customization get? Whats the sweet spot between maximum genercitiy and maximum hard-coded?