Skip to main content

Use custom objects in HTML templates

Zuora

Use custom objects in HTML templates

You can use custom objects and fields in HTML templates to display more complex or advanced data in billing documents, including invoices, credit memos, and debit memos.

To generate PDFs from HTML templates that use custom objects, you must have the "View Custom Object" permission.

This tutorial takes invoices as an example; the configuration procedure is similar for credit memos and debit memos.

When using custom objects and fields in HTML templates, keep the following key points in mind:

  • Object names must be made plural when they are used in HTML templates.
    If you define a custom object name as Messages, you have to use default__messageses to refer to the object list. The following table lists more examples.
    API names of custom objects Names to refer to object lists in HTML templates
    Messages default__messageses
    Entites default__entiteses
    Reefer default__reefers
    ExchangeRate default__exchangerates
    LegalEntity default__legalentities
  • The custom fields used in a filter must be filterable.
    For example, if you want to retrieve a list of reefers that belong to a subscription, the custom field SubscriptionName__c contained in {{#default__reefers|FilterByRef(SubscriptionName__c,EQ,SubscriptionNumber)}} must be filterable.

Display messages based on locales

You can store translation messages in custom objects including locales and message keys as filterable fields. Later, you can query the translation messages based on message keys and locales, and use them as field labels, table header names, or texts in the template.

To display messages based on locales, perform the following steps:

  1. Define a custom object named messages to store the messages.
    The following table lists the structures of the custom object. For example, KEY is the field label, and key__c is its API name. The fields key__c and locale__c are filterable.

    ID KEY(key__c) LOCALE(locale__c) VALUE(value__c)
    0f9e8dc6-4256-4185-805a-efbb761245a4 invoice_date en_US Invoice Date
    0f9e8dc6-4256-4185-805a-efbb761245a5 payment_terms en_US Payment Terms
    0f9e8dc6-4256-4185-805a-efbb761245a9 invoice_date zh_CN 账单日
    0f9e8dc6-4256-4185-805a-efbb76124510 payment_terms zh_CN 账期
  2. Use filter functions to query the message value based on keys. {{#default__messageses|FilterByValue(locale__c,EQ,zh_CN)|FilterByValue(key__c,EQ,invoice_date)}}{{value__c}}:{{/default__messageses|FilterByValue(locale__c,EQ,zh_CN)|FilterByValue(key__c,EQ,invoice_date)}}

Display lists of custom object records

To display a list of custom object records in invoices, perform the following steps:

  1. Define a custom object named reefer to store the reefers that each subscription has.
    One subscription can have multiple reefers. The following table lists the structures of the custom object. The custom object uses Subscription Name as the key, and its API name is SubscriptionName__c.

    ID Brand Number Volume StartDate Subscription Name
    0f9e8dc6-4256-4185-805a-efbb761245a4 Brand1 10003 50m~3 2021-04-01 A-S00000056
    0f9e8dc6-4256-4185-805a-efbb761245a5 Brand2 10002 100m~3 2021-01-01 A-S00000054
    0f9e8dc6-4256-4185-805a-efbb761245a9 Brand3 10001 100m~3 2021-01-01 A-S00000056
  2. Use the HTML component to configure how to display the reefers that an invoice contains. For more information about the configuration procedure, see Configure HTML codes in HTML templates.

    • Use {{#InvoiceItems|Map(Subscription)|Uniq}} to construct the subscription list that the invoice contains.
    • For each subscription, filter reefers based on the subscription number: {{#default__reefers|FilterByRef(SubscriptionName__c,EQ,SubscriptionNumber)}}
    • For each reefer record, display its fields as a row in the table.

    The following information is an example of HTML codes. The style part is ignored.

    <style>
        ...
    </style>
    {{#Invoice}}
    <table class="table-grid u_content_custom_generic_table_2">
    <thead><tr>
         <th style="width:auto; ">
             Subscription Name
         </th>
         <th style="width:auto; ">
             Brand
         </th>
         <th style="width:auto; ">
             Number
         </th>
         <th style="width:auto; text-align:right;">
             Volume
         </th>
         <th style="width:auto; text-align:right;">
             Start Date
         </th></tr></thead>
    <tbody>
    {{#InvoiceItems|Map(Subscription)|Uniq}}
       {{Cmd_Assign(SubscriptionNumber,Name)}}
    
    
       {{#default__reefers|FilterByRef(SubscriptionName__c,EQ,SubscriptionNumber)}}
       <tr>
         <td style="">{{SubscriptionName__c}}</td>
         <td style="">{{Brand__c}}</td>
         <td style="">{{Number__c}}</td>
         <td style="">{{Volume__c}}</td>
         <td style="">{{StartDate__c}}</td>
       </tr>
       {{/default__reefers|FilterByRef(SubscriptionName__c,EQ,SubscriptionNumber)}}
    {{/InvoiceItems|Map(Subscription)|Uniq}}
    
    
       </tbody>
    </table>
    {{/Invoice}}
    

The following image shows the preview result.

Display exchange rates on invoices

Assume that your customers have AED as the home currency, but are billed in USD. You want to present the charge amount and tax in AED on the invoice. The exchange rate that is used to convert from USD to AED is also displayed.

To display exchange rates on invoices, perform the following steps:

  1. Define a custom object named ExchangeRate to store the exchange rate.
    The following table lists the structures of the custom object. The From__c and To__c fields are filterable.

ID

From (From__c)

To (To__c)

Rate(Rate__c)

0f9e8dc6-4256-4185-805a-efbb761245a4

USD

AED

8.00

0f9e8dc6-4256-4185-805a-efbb761245a5

USD

GBP

0.73

0f9e8dc6-4256-4185-805a-efbb761245a9

CAD

GBP

1.09

  1. Use filter functions to query the exchange rate based on home currency (AED) and billed currency (USD).
    You can use the Cmd_Assign command to define a global variable named TheRate, which can be used anywhere in the template.

    {{#default__exchangerates|FilterByRef(From__c,EQ,Invoice.Account.Currency)|FilterByValue(To__c,EQ,AED)|First(1)}}

    {{Cmd_Assign(TheRate,Rate__c,True)}}

    {{/default__exchangerates|FilterByRef(From__c,EQ,Invoice.Account.Currency)|FilterByValue(To__c,EQ,AED)|First(1)}}

  2. Use the TheRate global variable to calculate all amount fields in home currency. 
    The following example is to calculate the total tax amount in home currency.
    {{Account.Currency|Symbol}}{{TaxAmount|Round(2)|Localise}} ( {{#Wp_Eval}} {{TaxAmount}}*{{TheRate}}|Round(2)|Localise {{/Wp_Eval}} AED)
    The output is displayed as $1.18 ( 9.44 AE).
    The following example HTML codes are to show an invoice item as a row in a charge details table.

    {{#InvoiceItems}}
    <tr>
    <td style="">{{ChargeName}}
    </td>
    <td style="">{{ServiceStartDate|Localise}} - {{ServiceEndDate|Localise}}
    </td>
    <td style="text-align:right;">{{Account.Currency|Symbol}}{{ChargeAmount|Round(2)|Localise}} ({{#Wp_Eval}}{{ChargeAmount}}*{{TheRate}}|Round(2)|Localise{{/Wp_Eval}} AED)
    </td>
    <td style="text-align:right;">{{Account.Currency|Symbol}}{{TaxAmount|Round(2)|Localise}} ({{#Wp_Eval}}{{TaxAmount}}*{{TheRate}}|Round(2)|Localise{{/Wp_Eval}} AED)
    </td>
    <td style="text-align:right;">{{Account.Currency|Symbol}}{{#Wp_Eval}}{{ChargeAmount}}+{{TaxAmount}}|Round(2)|Localise{{/Wp_Eval}} ({{#Wp_Eval}}({{ChargeAmount}}+{{TaxAmount}})*{{TheRate}}|Round(2)|Localise{{/Wp_Eval}} AED)
    </td>
    </tr>
    {{/InvoiceItems}}
    
  3. Preview the template after you add the merge fields. 

The following image shows an example of the preview result.

use_custom_object_HTML_templates_display_exchange_rate.png

Use custom objects with relationships with Zuora objects

Assume that your company has multiple legal entities globally, and you want to associate your customer accounts with appropriate legal entities. The legal entity information is used to display information conditionally on generated invoice PDF files. 

To use custom objects with relationships with Zuora objects in your scenario, perform the following steps:

  1. Define a custom object named LegalEntity to store the legal entity information.
    The custom object has a custom field called Account__c with Field Type as Relationship, Namespace as com_zuora, and Object Name as Account. The relationship between the custom object LegalEntity and its related object Account is many-to-one, although one Account object is related to only oneLegalEntity object.
    The following table lists the structures of the custom object. The EntityNumber__c and Account__c fields are filterable.
    ID Entity Name (EntityName__c) Entity Number (EntityNumber__c) Account (Account__c)
    0f9e8dc6-4256-4185-805a-efbb761245a4 Legal Entity USA 100001 2c92c8fe79652c000179683d2c1f122d
    0f9e8dc6-4256-4185-805a-efbb761245a5 Legal Entity UK 100002 2c92c8fb783a5faa01783ae11bdc0731
    0f9e8dc6-4256-4185-805a-efbb761245a9 Legal Entity APAC 100003 2c92c8fb7d2c85e1017d2d18bd9d0cda
  2. To display entity names on  invoices, use the following example:
    {{#Account.default__legalentities|First(1)}}
    {{EntityName__c}}
    {{/Account.default__legalentities|First(1)}}

    In the preceding example, Account.default__legalentities refers to the custom object list, and the First function returns the first one record.

  3. To display tax information by entity number, use the following example: 
    {{#Invoice}}
    {{#Account.default__legalentities|First(1)}}
    {{Cmd_Assign(VarEntityNumber,EntityNumber__c,True)}}
    {{/Account.default__legalentities|First(1)}}
    {{#Wp_Eval}}"{{VarEntityNumber}}" == "100001" ? "show Legal Entity USA tax info" : ""{{/Wp_Eval}}
    {{#Wp_Eval}}"{{VarEntityNumber}}" == "100002" ? "show Legal Entity UK tax info" : ""{{/Wp_Eval}}
    {{/Invoice}}

    The preceding example assigns EntityNumber__c to a global variable, and uses the global variable in later expressions. The expressions evaluate entity numbers, and show tax information appropriately. For more information, see Variables and Expressions.

  4. To display the custom fields of a custom object in a table, use the following example:
    {{#Invoice.Account.default__legalentities|First(1)}}
    {{EntityName__c}}
    {{/Invoice.Account.default__legalentities|First(1)}}

    In a Data Table component, assume that you select InvoiceItems as Table Object. Then, you can add a new column with Advanced Options set to on, and use the preceding merge field example for the column. For more information, see Configuring data tables in HTML invoice templates.

Notes

If you use one custom object multiple times in a template or use multiple decorators like FilterByValue and FilterByRefon such objects, you cannot get the PDF generated as expected. For example, assume that you define a custom object named resources, and the filter function examples are as follows:

{{#default__resources|FilterByValue(Key__c,EQ,SupplierVATId)|FilterByRef(Country__c,EQ,Invoice.Account.SoldTo.Country)}} {{Invoice.Account.SoldTo.Country}} {{Label__c}}: {{Value__c}} {{/default__resources|FilterByValue(Key__c,EQ,SupplierVATId)|FilterByRef(Country__c,EQ,Invoice.Account.SoldTo.Country)}}

{{#default__resources|FilterByValue(Key__c,EQ,SupplierPSTId)|FilterByRef(State__c,EQ,Invoice.Account.SoldTo.State)}} {{Label__c}}: {{Value__c}} {{/default__resources|FilterByValue(Key__c,EQ,SupplierPSTId)|FilterByRef(State__c,EQ,Invoice.Account.SoldTo.State)}} 

You will receive an error, saying that "Could not generate PDF: Exception while fetching data (/default__resources) : com.zuora.owl.hawk.exception.HawkException: Bad argument: Your Filter is not supported for current object type [ErrorCode: 412 QueryFailed] [85B30716DE46310C]." 

To generate the PDF files and avoid missing custom object data on the generated PDF files, you can use the Cmd_Assign command to have the custom object get assigned to a variable. Note that you must have the required permissions to access the custom objects. For more information about the Cmd_Assign command, see Merge field syntax for HTML templates.

Assume that you define a custom object named resources, which has four custom fields called Entity, Key, Value, Label. You can assign the custom object to variable along with the available custom fields by using the Cmd_Assign command:

{{Cmd_Assign(Resources1,default__resources|FilterByValue(Entity__c,NOT_NULL)|FilterByValue(Key__c,NOT_NULL)|FilterByValue(Value__c,NOT_NULL)|FilterByValue(Label__c,NOT_NULL),true)}}

The following example shows how you can apply the required decorators on the variable Resources1 in the template:

{{#Invoice}} {{Cmd_Assign(Resources1,default__resources|FilterByValue(Entity__c,NOT_NULL)|FilterByValue(Key__c,NOT_NULL)|FilterByValue(Value__c,NOT_NULL)|FilterByValue(Label__c,NOT_NULL),true)}} {{#Resources1|FilterByValue(Key__c,EQ,'Zuora')|FilterByValue(Entity__c,EQ,'Zuora Inc')}}
{{Label__c}}: {{Value__c}}
{{/Resources1|FilterByValue(Key__c,EQ,'OrgName')|FilterByValue(Entity__c,EQ,'Zuora Inc')}} {{Cmd_Assign(Resources1,default__resources,true)}} {{#Resources1|FilterByValue(Key__c,EQ,'Address1')|FilterByValue(Entity__c,EQ,'SomeValue')}}
{{Label__c}}: {{Value__c}}
{{/Resources1|FilterByValue(Key__c,EQ,'Address1')|FilterByValue(Entity__c,EQ,'SomeValue')}}
{{/Invoice}}

Through using the Cmd_Assign command, you can avoid the PDF file generating issue when you use one custom object multiple times in a template or use multiple decorators like FilterByValue and FilterByRefon such objects. Also, with this Cmd_Assign command workaround, you will not have custom object data missing on the PDF files.