Advanced PDF Templates
Create professional, branded PDF documents for invoices, purchase orders, and other transactions.
Overview
ADVANCED PDF TEMPLATE SYSTEM
-------------------------------------------------------------------------------
What It Is:
- Built-in PDF template engine using BFO (Big Faceless Organization)
- XML/FreeMarker-based templates
- Access to record data via template variables
- CSS-like styling support
Best For:
- Custom invoice layouts
- Branded purchase orders
- Professional packing slips
- Customer statements
- Any standard transaction document
Navigation: Customization > Forms > Advanced PDF/HTML Templates
Step-by-Step Tutorials
Tutorial 1: Creating Your First Advanced PDF Template
Goal: Create a custom invoice template from scratch.
Step 1: Navigate to Advanced PDF Templates
NAVIGATION PATH
-------------------------------------------------------------------------------
Home > Customization > Forms > Advanced PDF/HTML Templates
+-----------------------------------------------------------------------------+
| ADVANCED PDF/HTML TEMPLATES [New] [Import] |
+-----------------------------------------------------------------------------+
| Name | Type | Transaction | Preferred |
+-------------------------+----------------+-----------------+---------------+
| Standard Invoice | Transaction | Invoice | Yes |
| Standard Sales Order | Transaction | Sales Order | Yes |
| Custom Invoice - A | Transaction | Invoice | No |
+-----------------------------------------------------------------------------+
Click [New] to create a new template
Step 2: Configure Template Settings
NEW ADVANCED PDF/HTML TEMPLATE
-------------------------------------------------------------------------------
+-----------------------------------------------------------------------------+
| PRIMARY INFORMATION |
+-----------------------------------------------------------------------------+
| |
| Name*: [My Custom Invoice ] |
| |
| Type*: [Transaction v] |
| Options: Transaction, Statement, Custom |
| |
| Transaction*: [Invoice v] |
| (Only shows when Type = Transaction) |
| |
+-----------------------------------------------------------------------------+
| PREFERENCES |
+-----------------------------------------------------------------------------+
| |
| [ ] Preferred |
| Check to make this the DEFAULT template for this transaction type |
| |
| [x] Available Without Login |
| Allow access via public links (customer portal) |
| |
+-----------------------------------------------------------------------------+
[!IMPORTANT] Only ONE template can be marked "Preferred" per transaction type. The preferred template is used when no specific template is assigned to a form.
Step 3: Enter Template Content
TEMPLATE EDITOR TAB
-------------------------------------------------------------------------------
The editor provides a text area for XML/FreeMarker code:
+-----------------------------------------------------------------------------+
| SOURCE CODE EDITOR |
+-----------------------------------------------------------------------------+
| |
| <?xml version="1.0"?> |
| <!DOCTYPE pdf PUBLIC "-//big.faceless.org//report" "report-1.1.dtd"> |
| <pdf> |
| <head> |
| <link name="NotoSans" type="font" subtype="truetype" |
| src="${nsfont.NotoSans_Regular}" /> |
| <style type="text/css"> |
| body { font-family: NotoSans, sans-serif; font-size: 10pt; } |
| .header { font-size: 18pt; font-weight: bold; } |
| .right { text-align: right; } |
| </style> |
| </head> |
| <body size="Letter" padding="0.5in"> |
| <h1 class="header">INVOICE</h1> |
| <p>Invoice #: ${record.tranid}</p> |
| <p>Date: ${record.trandate}</p> |
| <p>Customer: ${record.entity}</p> |
| </body> |
| </pdf> |
| |
+-----------------------------------------------------------------------------+
| [Save] [Preview] [Cancel] |
+-----------------------------------------------------------------------------+
Step 4: Preview Your Template
PREVIEW WORKFLOW
-------------------------------------------------------------------------------
1. Click [Preview] button
2. Select a real transaction to use as sample data:
+-----------------------------------------------------------------------+
| SELECT PREVIEW TRANSACTION |
+-----------------------------------------------------------------------+
| |
| Transaction Type: Invoice |
| |
| Select Transaction: [INV-00123 - ABC Corp v] |
| |
| [Preview] [Cancel] |
+-----------------------------------------------------------------------+
3. PDF opens in new browser tab/window
4. Review and iterate until satisfied
Step 5: Save the Template
After previewing:
1. Click [Save] to save the template
2. Template now appears in the list
3. Status: Not yet linked to any form (we'll do that next)
Tutorial 2: Linking Template to a Custom Form
Goal: Make your custom template appear when printing from a specific transaction form.
Step 1: Navigate to Transaction Forms
NAVIGATION PATH
-------------------------------------------------------------------------------
For Invoice forms:
Customization > Forms > Transaction Forms
Filter by: [Invoice v]
+-----------------------------------------------------------------------------+
| TRANSACTION FORMS [New] [Customize] |
+-----------------------------------------------------------------------------+
| Name | Type | Preferred | Standard | |
+---------------------------+-----------+-----------+----------+-------------+
| Standard Invoice Form | Invoice | Yes | Yes | [Customize] |
| Custom Invoice - Retail | Invoice | No | No | [Edit] |
| Custom Invoice - Wholesale| Invoice | No | No | [Edit] |
+-----------------------------------------------------------------------------+
Step 2: Edit/Customize the Form
EDITING A TRANSACTION FORM
-------------------------------------------------------------------------------
Click [Edit] on your custom form (or [Customize] on a standard form)
+-----------------------------------------------------------------------------+
| TRANSACTION FORM: Custom Invoice - Retail |
+-----------------------------------------------------------------------------+
| [MAIN] [SCREEN FIELDS] [ACTIONS] [PRINTING] [ROLES] |
+-----------------------------------------------------------------------------+
Click the [PRINTING] tab
Step 3: Assign PDF Template (THE KEY STEP)
PRINTING TAB
-------------------------------------------------------------------------------
+-----------------------------------------------------------------------------+
| PRINTING OPTIONS |
+-----------------------------------------------------------------------------+
| |
| Print Template: |
| +-------------------------------------------------------------------------+ |
| | ADVANCED TEMPLATE*: [My Custom Invoice v] <-- SELECT HERE | |
| | | |
| | Options: | |
| | - Standard Invoice (default) | |
| | - My Custom Invoice <-- Your new template | |
| | - Custom Invoice - Premium | |
| +-------------------------------------------------------------------------+ |
| |
| HTML Template: [Standard Invoice HTML v] |
| (For email body, screen display) |
| |
| [ ] Enable Transaction Printing |
| Allows printing directly from transaction |
| |
| [x] Show Print Button |
| |
+-----------------------------------------------------------------------------+
| [Save] [Cancel] |
+-----------------------------------------------------------------------------+
[!TIP] The Advanced Template field determines which PDF template is used when:
- User clicks "Print" on the transaction
- User selects "PDF" output
- System generates documents for email
Step 4: Save and Test
TESTING THE LINK
-------------------------------------------------------------------------------
1. Save the form
2. Open an Invoice that uses this form
3. Click Actions > Print (or the Print button)
+-----------------------------------------------------------------------+
| PRINT OPTIONS |
+-----------------------------------------------------------------------+
| |
| Output Format: |
| (o) PDF |
| ( ) HTML |
| |
| Template: My Custom Invoice (auto-selected from form) |
| |
| [Print] [Cancel] |
+-----------------------------------------------------------------------+
4. Your custom template should render!
Tutorial 3: Creating Form-Specific Templates
Scenario: Different customer types need different invoice layouts.
MULTIPLE FORMS, MULTIPLE TEMPLATES
-------------------------------------------------------------------------------
RETAIL CUSTOMERS WHOLESALE CUSTOMERS
------------------- --------------------
Form: Invoice - Retail Form: Invoice - Wholesale
| |
+--> Template: Invoice-Retail-PDF +--> Template: Invoice-Wholesale-PDF
- Simple layout - Detailed pricing
- Consumer-friendly - Volume discounts shown
- Payment QR code - Terms & conditions
When user selects form, When user selects form,
matching template auto-applies matching template auto-applies
Implementation Steps
STEP 1: Create Both Templates
-------------------------------------------------------------------------------
Template 1: Invoice-Retail-PDF
- Type: Transaction
- Transaction: Invoice
- Preferred: No
Template 2: Invoice-Wholesale-PDF
- Type: Transaction
- Transaction: Invoice
- Preferred: No
STEP 2: Create Both Forms
-------------------------------------------------------------------------------
Form 1: Invoice - Retail
- Customize Standard Invoice Form
- Printing Tab > Advanced Template: Invoice-Retail-PDF
- Roles Tab: Assign to Sales - Retail role
Form 2: Invoice - Wholesale
- Customize Standard Invoice Form
- Printing Tab > Advanced Template: Invoice-Wholesale-PDF
- Roles Tab: Assign to Sales - Wholesale role
RESULT:
-------------------------------------------------------------------------------
+-- User with "Sales - Retail" role
| +-- Creates Invoice
| +-- Form auto-selected: Invoice - Retail
| +-- Print uses: Invoice-Retail-PDF
|
+-- User with "Sales - Wholesale" role
+-- Creates Invoice
+-- Form auto-selected: Invoice - Wholesale
+-- Print uses: Invoice-Wholesale-PDF
Linked Form Templates - Special Cases
Case 1: Subsidiary-Specific Templates
Scenario: Each subsidiary has different branding/logos.
SUBSIDIARY-BASED TEMPLATES
-------------------------------------------------------------------------------
Parent Company (Subsidiary ID: 1)
+-- Form: Invoice - Parent
| +-- Template: Invoice-Parent-PDF (Parent logo, US address)
|
+-- Subsidiary A (ID: 2)
| +-- Form: Invoice - Sub A
| +-- Template: Invoice-SubA-PDF (Sub A logo, UK address)
|
+-- Subsidiary B (ID: 3)
+-- Form: Invoice - Sub B
+-- Template: Invoice-SubB-PDF (Sub B logo, EU address)
Configuration Steps
1. Create template for each subsidiary:
- Invoice-Parent-PDF
- Invoice-SubA-PDF
- Invoice-SubB-PDF
2. Create form for each subsidiary:
Customization > Forms > Transaction Forms > Customize Standard Invoice
Form: Invoice - Parent
Main Tab:
Subsidiary: [Parent Company]
Printing Tab:
Advanced Template: [Invoice-Parent-PDF]
Form: Invoice - Sub A
Main Tab:
Subsidiary: [Subsidiary A]
Printing Tab:
Advanced Template: [Invoice-SubA-PDF]
3. Result:
Transactions for each subsidiary automatically use the correct form/template
Case 2: Customer-Specific Templates via Script
Scenario: VIP customers get a premium invoice layout.
/**
* User Event Script - Dynamic Template Selection
* @NApiVersion 2.1
* @NScriptType UserEventScript
*/
define(['N/record', 'N/render'], function(record, render) {
function beforeLoad(context) {
if (context.type !== context.UserEventType.PRINT) return;
var invoice = context.newRecord;
var customerId = invoice.getValue('entity');
// Load customer to check VIP status
var customer = record.load({
type: record.Type.CUSTOMER,
id: customerId
});
var isVIP = customer.getValue('custentity_vip_customer');
if (isVIP) {
// Override the template for VIP customers
var vipTemplate = render.mergeEmail({
templateId: 'custpdf_vip_invoice' // Custom template internal ID
});
// Apply custom rendering logic
}
}
return {
beforeLoad: beforeLoad
};
});
Case 3: Conditional Template Based on Transaction Data
Scenario: Orders over $10,000 need additional terms and signature block.
/**
* Template with FreeMarker Conditional Logic
* This is handled WITHIN the template itself
*/
<?xml version="1.0"?>
<!DOCTYPE pdf PUBLIC "-//big.faceless.org//report" "report-1.1.dtd">
<pdf>
<head>
<style type="text/css">
.terms { font-size: 8pt; border: 1px solid #ccc; padding: 10px; }
.signature { border-top: 1px solid black; width: 200px; margin-top: 50px; }
</style>
</head>
<body size="Letter">
<!-- Standard invoice content -->
<h1>INVOICE</h1>
<p>Invoice #: ${record.tranid}</p>
<p>Total: ${record.total}</p>
<!-- CONDITIONAL: Show extended terms for orders over $10,000 -->
<#if record.total?number gt 10000>
<div class="terms">
<h3>Extended Payment Terms</h3>
<p>For orders exceeding $10,000, the following terms apply:</p>
<ul>
<li>50% due upon signing</li>
<li>50% due upon delivery</li>
<li>Net 30 applies to remaining balance</li>
</ul>
</div>
<div class="signature">
<p>Customer Signature: ___________________</p>
<p>Date: ___________________</p>
</div>
</#if>
</body>
</pdf>
Case 4: Default Template vs Form Template Priority
TEMPLATE PRIORITY ORDER
-------------------------------------------------------------------------------
When printing, NetSuite selects templates in this order:
1. FORM-ASSIGNED TEMPLATE (highest priority)
| If form has "Advanced Template" set in Printing tab,
| that template is used.
|
+-- If no form template...
|
2. PREFERRED TEMPLATE
| Template marked as "Preferred" for that transaction type
| (only one per type)
|
+-- If no preferred template...
|
3. STANDARD TEMPLATE (lowest priority)
NetSuite's built-in default template
Case 5: Multiple Transaction Types in One Template
Scenario: Unified template for all sales documents.
<?xml version="1.0"?>
<!DOCTYPE pdf PUBLIC "-//big.faceless.org//report" "report-1.1.dtd">
<pdf>
<body>
<!-- Dynamic title based on transaction type -->
<#if record.type == "invoice">
<h1>INVOICE</h1>
<#elseif record.type == "salesorder">
<h1>SALES ORDER</h1>
<#elseif record.type == "estimate">
<h1>QUOTE</h1>
<#else>
<h1>DOCUMENT</h1>
</#if>
<!-- Common content for all types -->
<p>Document #: ${record.tranid}</p>
<p>Date: ${record.trandate}</p>
<p>Customer: ${record.entity}</p>
<!-- Items table (common structure) -->
<table>
<#list record.item as item>
<tr>
<td>${item.item}</td>
<td>${item.quantity}</td>
<td>${item.rate}</td>
<td>${item.amount}</td>
</tr>
</#list>
</table>
<!-- Transaction-specific footer -->
<#if record.type == "invoice">
<p>Payment Due: ${record.duedate}</p>
<#elseif record.type == "salesorder">
<p>Expected Ship Date: ${record.shipdate!""}</p>
</#if>
</body>
</pdf>
Template Structure Reference
Basic XML Structure
<?xml version="1.0"?>
<!DOCTYPE pdf PUBLIC "-//big.faceless.org//report" "report-1.1.dtd">
<pdf>
<head>
<!-- Font definitions -->
<link name="NotoSans" type="font" subtype="truetype"
src="${nsfont.NotoSans_Regular}" />
<link name="NotoSansBold" type="font" subtype="truetype"
src="${nsfont.NotoSans_Bold}" />
<!-- CSS styling -->
<style type="text/css">
body {
font-family: NotoSans, sans-serif;
font-size: 10pt;
}
/* More styles... */
</style>
</head>
<body size="Letter" padding="0.5in 0.5in 0.5in 0.5in">
<!-- Template content here -->
</body>
</pdf>
Available Fonts
| Font Name | Variable |
|---|---|
| NotoSans Regular | ${nsfont.NotoSans_Regular} |
| NotoSans Bold | ${nsfont.NotoSans_Bold} |
| NotoSans Italic | ${nsfont.NotoSans_Italic} |
| NotoSans CJK (Chinese/Japanese/Korean) | ${nsfont.NotoSansCJK_Regular} |
Page Sizes
| Size | Dimensions |
|---|---|
| Letter | 8.5" x 11" |
| A4 | 210mm x 297mm |
| Legal | 8.5" x 14" |
| Tabloid | 11" x 17" |
Common Template Variables
Transaction Header Fields
HEADER FIELDS
-------------------------------------------------------------------------------
| Variable | Description |
|-----------------------------|-----------------------------------------------|
| ${record.tranid} | Transaction number (INV-00001) |
| ${record.trandate} | Transaction date |
| ${record.entity} | Customer/Vendor name |
| ${record.status} | Transaction status |
| ${record.memo} | Memo field |
| ${record.subsidiary} | Subsidiary name |
| ${record@type} | Record type (invoice, salesorder, etc.) |
Address Fields
ADDRESS FIELDS
-------------------------------------------------------------------------------
Billing Address:
| ${record.billaddress} | Full formatted address |
| ${record.billaddressee} | Addressee name |
| ${record.billaddr1} | Address line 1 |
| ${record.billaddr2} | Address line 2 |
| ${record.billcity} | City |
| ${record.billstate} | State/Province |
| ${record.billzip} | Postal code |
| ${record.billcountry} | Country |
Shipping Address:
| ${record.shipaddress} | Full formatted address |
| ${record.shipaddressee} | Addressee name |
| ${record.shipaddr1} | Address line 1 |
| ... (same pattern as billing) |
Line Items
<!-- Loop through item lines -->
<#list record.item as item>
<tr>
<td>${item.item}</td> <!-- Item name -->
<td>${item.description}</td> <!-- Description -->
<td>${item.quantity}</td> <!-- Quantity -->
<td>${item.units}</td> <!-- UoM -->
<td>${item.rate}</td> <!-- Unit price -->
<td>${item.amount}</td> <!-- Line total -->
<td>${item.taxcode}</td> <!-- Tax code -->
<td>${item.custcol_field}</td> <!-- Custom column field -->
</tr>
</#list>
Custom Fields
CUSTOM FIELD ACCESS
-------------------------------------------------------------------------------
Body Fields (custbody_xxx):
${record.custbody_po_number}
${record.custbody_project_name}
${record.custbody_shipping_method}
Column Fields (custcol_xxx) - inside item loop:
${item.custcol_serial_number}
${item.custcol_lot_number}
${item.custcol_warranty_date}
Custom Record Fields:
${record.custrecord_xxx}
Including Custom Record Data in PDFs
Accessing Custom Records
When a transaction has a field linked to a custom record, access it using the @ syntax:
<!-- If custbody_project links to a custom record -->
<p>Project Name: ${record.custbody_project}</p>
<p>Project Manager: ${record.custbody_project@custrecord_proj_manager}</p>
<p>Project Code: ${record.custbody_project@custrecord_proj_code}</p>
Accessing Child Records (Related Lists)
Use recmach prefix to access child records where "Record is Parent" is checked:
<!-- List shipping instructions attached to this transaction -->
<#if record.recmachcustrecord_si_transaction?has_content>
<h3>Shipping Instructions</h3>
<table>
<tr>
<th>Type</th>
<th>Description</th>
<th>Priority</th>
</tr>
<#list record.recmachcustrecord_si_transaction as instruction>
<tr>
<td>${instruction.custrecord_si_type}</td>
<td>${instruction.custrecord_si_desc}</td>
<td>${instruction.custrecord_si_priority}</td>
</tr>
</#list>
</table>
</#if>
[!NOTE] The
recmachprefix is followed by the field ID on the child record that links to the parent (the "Record is Parent" field).
Complete Example: Invoice with Custom Gift Items
<?xml version="1.0"?>
<!DOCTYPE pdf PUBLIC "-//big.faceless.org//report" "report-1.1.dtd">
<pdf>
<head>
<link name="NotoSans" type="font" subtype="truetype"
src="${nsfont.NotoSans_Regular}" />
<style type="text/css">
body { font-family: NotoSans; font-size: 10pt; }
.gift-table { background: #fffbe6; border: 1px solid #ffc107; }
.gift-table th { background: #ffc107; }
</style>
</head>
<body size="Letter" padding="0.5in">
<h1>INVOICE ${record.tranid}</h1>
<!-- Standard Items -->
<h3>Items Ordered</h3>
<table>
<tr><th>Item</th><th>Qty</th><th>Price</th><th>Total</th></tr>
<#list record.item as item>
<tr>
<td>${item.item}</td>
<td>${item.quantity}</td>
<td>${item.rate}</td>
<td>${item.amount}</td>
</tr>
</#list>
</table>
<!-- Custom Record: Gift Items (linked via recmach) -->
<#if record.recmachcustrecord_gift_transaction?has_content>
<h3>Complimentary Gift Items</h3>
<table class="gift-table">
<tr><th>Gift</th><th>Message</th></tr>
<#list record.recmachcustrecord_gift_transaction as gift>
<tr>
<td>${gift.name}</td>
<td>${gift.custrecord_gift_message!""}</td>
</tr>
</#list>
</table>
</#if>
</body>
</pdf>
Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Template not showing in form | Template not saved or wrong type | Verify template Type matches form's transaction |
Variables show as ${...} | Typo in variable name | Check exact field ID in Records Catalog |
| Page breaks unexpectedly | Content overflows | Add page-break-inside: avoid CSS |
| Images not loading | Wrong path or permissions | Use /images/ File Cabinet path |
| Fonts not rendering | Font not linked | Add <link> in <head> section |
Debug Tips
<!-- Debug: Show all available fields -->
<#list record?keys as key>
<p>${key}: ${record[key]!""}</p>
</#list>
<!-- Debug: Check if field exists -->
<#if record.custbody_myfield??>
<p>Field exists: ${record.custbody_myfield}</p>
<#else>
<p>Field does not exist or is null</p>
</#if>
Best Practices
- Always test with real data - Use Preview with actual transactions
- Use null-safe access -
${field!""}or${field!"Default"} - Organize CSS in
<head>- Keep styling separate from content - Use classes not inline styles - Easier maintenance
- Consider page breaks - Plan for multi-page documents
- Test all subsidiaries - Verify currency, addresses, logos
- Version your templates - Use naming like
Invoice-v2,Invoice-v3
References
- PDF Customization Overview
- Suitelet PDF Generation - Script-driven PDFs
- Bulk PDF Generation - Batch processing
- Record Relationships - Custom record linking