How to Create a Custom Template Content

by Community Manager ‎10-12-2016 11:02 AM - edited ‎10-12-2016 11:14 AM

The Quote Template engine is highly flexible but does not always address all document output requirements out of the box. Sometimes, you might want to present the quote information in a completely different type of layout, or you might want to render data from custom objects.

 

To address these use cases, you might have to write a custom Template Content, which involves three major steps. This article takes you through such a journey, that will display Quote Line Groups in columns instead of on top of each other. This is only one relatively simple example of what can be done with custom Template Contents. A more complex version of this example could use the Line Columns defined in the Quote Template to provide a truly dynamic approach to a column layout.

 

Here are the steps:

  • Create the Template Content record
  • Create a VisualForce page
  • Create an Apex Controller class

 

Template Content Creation

 

Create a new Template Content record, of type “Custom”, and configure it as per the screenshot below:

 

Custom Quote Template.png

 

 

VisualForce Page

 

Create a new VisualForce page called “HorizontalGroupSection”, with the following content:

 

 

<apex:page showHeader="false" sidebar="false" cache="false" contentType="text/xml" controller="HorizontalGroupController">
    <block>
    <table table-layout="fixed" width="100%" border-color="#000000" border-width="1px" border-style="solid">
        <table-column column-width="{!labelColumnWidth}%"/>
        <apex:repeat var="option" value="{!options}">
            <table-column column-width="{!dataColumnWidth}%"/>
        </apex:repeat>

        <table-header>
            <table-row height="40pt" font-weight="bold" text-align="center" color="#FFFFFF" background-color="#0070c0">
                <table-cell border="1px solid black" width="120pt" margin="0pt" padding="5pt" padding-top="25pt"><block>Option</block></table-cell>
                <apex:repeat var="option" value="{!options}">
                    <table-cell  width="180pt" border="1px solid black" margin="0pt" padding="5pt" padding-top="28pt">
                        <block>{!HTMLENCODE(option.Name)}</block>
                    </table-cell>
                </apex:repeat>
            </table-row>
        </table-header>

        <table-body>
            <table-row font-size="0.9em">
                <table-cell border="1px solid black" margin="0pt" padding="18pt" padding-left="5pt" padding-right="5pt">
                    <block>Solution Products and Services</block>
                </table-cell>
                <apex:repeat var="option" value="{!options}">
                    <table-cell border="1px solid black" background-color="#ffffcc">
                        <block margin="0pt" padding="18pt" padding-left="10pt" padding-right="10pt" line-height="1.5">
                        <apex:repeat var="line" value="{!option.SBQQ__LineItems__r}">
                            <block><apex:outputText value="{0, number, ###,###,###,###}"><apex:param value="{!line.SBQQ__Quantity__c}"/></apex:outputText> {!HTMLENCODE(line.SBQQ__ProductName__c)}</block>
                        </apex:repeat>
                        </block>
                    </table-cell>
                </apex:repeat>
            </table-row>
            <table-row font-size="0.9em">
                <table-cell border="1px solid black" margin="0pt" padding="5pt" padding-top="10px">
                    <block>Annual Price</block>
                </table-cell>
                <apex:repeat var="option" value="{!options}">
                    <table-cell text-align="center"  padding="18pt" padding-left="10pt" padding-right="10pt" border="1px solid black" background-color="#ffffcc">
                        <block><apex:outputText value="{0, number, $###,###,###,###}"><apex:param value="{!option.SBQQ__ListTotal__c}"/></apex:outputText> List</block>
                        <block font-weight="bold"><apex:outputText value="{0, number, $###,###,###,###}"><apex:param value="{!option.SBQQ__NetTotal__c}"/></apex:outputText> Discounted</block>                        
                    </table-cell>
                </apex:repeat>
            </table-row>
            <table-row font-size="0.9em">
                <table-cell border="1px solid black" margin="0pt" padding="5pt" padding-top="10px">
                    <block>Payment Terms</block>
                </table-cell>
                <apex:repeat var="option" value="{!options}">
                    <table-cell text-align="center"  background-color="#ffffcc" padding="18pt" padding-left="10pt" padding-right="10pt" border="1px solid black"><block>{!HTMLENCODE(option.Payment_Terms__c)}</block></table-cell>
                </apex:repeat>
            </table-row>
            <table-row font-weight="bold" font-size="0.9em">
                <apex:repeat var="option" value="{!options}">
                    <table-cell text-align="center"   background-color="#ffffcc" padding="18pt" padding-left="10pt" padding-right="10pt" border="1px solid black">
                     <block font-weight="bold"><apex:outputText value="{0, number, $###,###,###,###}"><apex:param value="{!option.x90_Day_Trial_Price__c}"/></apex:outputText></block>                        
                    </table-cell>
                </apex:repeat>
            </table-row>
        </table-body>
    </table>
    </block>
</apex:page>

 

Apex Class

 

Create an Apex class called “HorizontalGroupController”, with the following content:

 

 

public with sharing class HorizontalGroupController {
    public SBQQ__QuoteLineGroup__c[] options {get; private set;}

    public Decimal labelColumnWidth {get; private set;}
    public Decimal dataColumnWidth {get; private set;}

    public HorizontalGroupController() {
        Id quoteId = (Id)ApexPages.currentPage().getParameters().get('qid');
        options = [SELECT Name, SBQQ__ListTotal__c, SBQQ__NetTotal__c, Payment_Terms__c, (SELECT SBQQ__ProductName__c,  SBQQ__Quantity__c FROM SBQQ__LineItems__r ORDER BY SBQQ__Number__c) FROM SBQQ__QuoteLineGroup__c Where SBQQ__Quote__c = :quoteId];

        dataColumnWidth = (80 / options.size());
        labelColumnWidth = 100 - (dataColumnWidth * options.size());
    }
}

 

Specifically, observe how the ID of the quote is retrieved in the Apex class in order to query data specific to the quote being rendered.

Comments
by Mbarnard
‎01-17-2017 05:05 PM - edited ‎01-17-2017 05:06 PM

 Please note Payment_Terms__c appears to be a custom field that SteelBrick had implemented for their own internal use and is not a standard field from the package.

   - You will most likely have to remove it from the Apex Class

 

It also will need to be removed from the second to last <apex:repeat...> area in the visualforcepage.

   - If you are trying to use this as a quick test somewhere.

by CirrusCPQ_CK
on ‎01-26-2017 07:46 PM

Probably easier for people to follow if you switch the order up. You have to create the Apex Controller first, then the Visualforce Page, then the Template Content.

 

Is there any example code for displaying a Discount Schedule as a table (of tiers) related to a particular Quote Line?

by bcloutier
‎03-09-2017 01:19 PM - edited ‎03-29-2017 02:30 PM

I was able to get this example to work by making the following modifications:

 

1. Replace instances of Payment_Terms__c with SBQQ__BillingFrequency__c, in both VF Page and Apex Class (note, certain versions of CPQ may not have Billing Frequency, so you can substitute another Quote Line Group field of your choosing here)

2. Replace instance of x90_Day_Trial_Price__c with SBQQ__NetTotal__c in Apex Class

3. Use the following URL for the Template Content Custom Source field: https://c.na40.visual.force.com/apex/HorizontalGroupSection

4. Updating the sample quote to include groups (otherwise a Divide by 0 error appears)

by dennispalmer
on ‎03-29-2017 01:44 PM

^^ This "getting example to work" doesn't work either.  

 

- There is no SBQQ__BillingFrequency__c field on the Quote Line Group object.

 

Still getting "An error occurred rendering the document" error.

by bcloutier
on ‎03-29-2017 02:27 PM

Hi Dennis,

 

We must be using a different version of CPQ, as SBQQ__BillingFrequency__c is indeed a Quote Line Group field. (I've tested on 26.0.1, which is over a year old now.) Of course, you could substitue another Quote Line Group field that you do have.

 

I'll try to update my response to clarify which version I'm using.

by mseeley
on ‎04-05-2017 01:38 PM

I was able to get this example to work but I would like to add an image in to the visual force page I have tried documents static resources and external resources and always get a "Failed to load PDF document" error when trying to preview the document.  Has anyone successfully added images to a custom content component and how did you get it to work?

by dennispalmer
on ‎04-05-2017 01:42 PM

 

<block><external-graphic padding-top="-1px" width="1370px" content-height="scale-to-fit" content-width="scale-to-fit" src="url('https://c.xxxx.content.force.com/servlet/servlet.ImageServer?id=0154D0000008za7&oid=00D4D0000008kLR&lastMod=1490881311000')" /></block>

The image must be a document that is externally viewable.

 

Right click the image in the document record and copy the url.  Paste it in as the value of the src attribute above.

 

by lzuluaga
on ‎04-28-2017 01:44 PM

 I’m having while creating custom template content for the quote document.

 

  1. I’ve created my custom Template Content record that points to a VisualForce page, and added this content to the Quote Template.

 

case1.png

 

2. I’ve created the VisualForce page and an APEX controller which queries for Discount Schedule Line Items. If I output the discount schedule below with no additional markup it renders and outputs as expected. No problem.

 

case2.png

 

3. I’ve looked at the documentation HERE from the CPQ site on how to create the custom VisualForce content. Using the example in the docs, I’ve stripped out unnecessary markup to just the table elements. (I know I’m not using a table body but I shouldn’t need it). I’ve put the same {!discSchedule} output inside the table.

 

case3.png

 

4.When I try to render the document with the content above, the preview fails to load. Note that I’ve used a combination of simple XML elements and ANY markup in the content will crash the preview. That’s the problem. My end result would need to loop through all the discount schedules that get returned from the controller and build up one large table.

 

case4.png

 

 

If someone can provide any insight on how I’m supposed to use markup in the Custom Template Content that would be a huge help. I’ve followed their documentation and I’m still having no luck.

 

Thanks in advance.

by dennispalmer
on ‎04-28-2017 01:47 PM

You need a <block> wrapper around your table.

by lzuluaga
on ‎05-25-2017 03:54 PM

@dennispalmer I tried that yet when I try to load the template I still get "Fail to Load" . 

 

I also tried removing the controller

Simple Code.jpg

 but I still got a "Failure to Load" when trying to load the template. I don't undertand!!  

by dennispalmer
on ‎05-31-2017 08:53 AM

Don't get rid of the controller.  Here is one that works.

 

Visualforce Page

<apex:page showHeader="false" sidebar="false" cache="false" contentType="text/xml" controller="QuoteLineItemsController">
    <block page-break-before="always">
        <table table-layout="fixed" width="1370px" background-color="white" font-family="Helvetica, Sans-Serif">
            <table-column column-width="1370px" />
            <table-header>
                <table-row>
                    <table-cell width="1370px">
                        <block>This is my header.</block>
                    </table-cell>
                </table-row>
            </table-header>
            <table-footer>
                <table-row>
                    <table-cell width="1370px">
                        <block>This is my footer.</block>
                    </table-cell>
                </table-row>
            </table-footer>
            <table-body>
                <table-row>
                    <table-cell width="1370px">
	                    <block>This is a test.</block>
                    </table-cell>
                </table-row>
            </table-body>
        </table>
    </block>
</apex:page>

 

 

Controller

public class QuoteLineItemsController {
    public SBQQ__Quote__c quote {get; private set;}

    public QuoteLineItemsController() {
        // get quote id.
        string strQuoteId = ApexPages.currentPage().getParameters().get('qid');
        
        // get quote via quote id.
        List<SBQQ__Quote__c> quotes = [select id, SBQQ__ExpirationDate__c, Total_Bundles_Formula__c, Count_Surgery_Lines__c from SBQQ__Quote__c where id = :strQuoteId limit 1];
        
        // if we found a quote,
        if (quotes.size() > 0) {
            // set quote.
            quote = quotes[0];
        }
    }
}

 

by PC
2 weeks ago

Is it possible to hide a row based on a condition in VF template content page?

any input would be appreciated.

 

Thanks in advance,

Prasanthi

Contributors