Training Workshop

DataMagik
Document Designer

Create professional documents, labels & reports

Use arrow keys to navigate • Press S for speaker notes

What You'll Learn

  • Go template syntax — variables, conditionals, loops, functions
  • The template editor — HTML, CSS, JSON, and settings tabs
  • CSS styling — professional layouts, page breaks, print-ready
  • Barcodes & QR codes — Code 128, EAN-13, QR, Data Matrix
  • Charts — line, bar, pie, doughnut with Chart.js
  • Document generation — async PDF pipeline, settings, output formats
  • Version control — branches, commits, rollback, approvals
  • Script engine integration — generate documents from scripts
Hands-on: This training has 16 exercises. You'll build a real invoice template by the end.

Prerequisites

  • A DataMagik account with Builder permission
  • Basic familiarity with HTML (tags, attributes, structure)
  • Basic familiarity with CSS (selectors, properties, layout)
  • No Go programming experience needed
Think of it like mail merge: You write an HTML document with placeholder fields like {{.CustomerName}}, provide data in JSON format, and DataMagik merges them into a finished PDF. The {{ }} syntax is the template language — this training will teach you everything you need.

Section 2

Go Template Syntax

The building blocks of dynamic documents

The Double Curly Braces

Everything dynamic in a template lives inside {{ }} delimiters:

<h1>Invoice #{{.InvoiceNumber}}</h1>
<p>Customer: {{.CustomerName}}</p>
<p>Date: {{.InvoiceDate}}</p>

Anything outside {{ }} is plain HTML — rendered as-is.

The dot: The . (dot) represents the current data context. {{.FieldName}} accesses a field from your JSON data.

Accessing Data Fields

<!-- Simple field access -->
{{.CompanyName}}

<!-- Nested field access -->
{{.Customer.Name}}
{{.Customer.Address.City}}

<!-- Array element access -->
{{index .Items 0}}

<!-- Length of an array -->
{{len .Items}} items

Sample JSON

{
  "CompanyName": "Acme Corp",
  "Customer": {
    "Name": "Jane Smith",
    "Address": {
      "City": "Portland"
    }
  },
  "Items": ["Widget", "Gadget"]
}

Output

Acme Corp
Jane Smith
Portland
Widget
2 items

Variables & Comments

Variables

Store values in variables with := for reuse:

{{$customerName := .Customer.Name}}
{{$total := printf "%.2f" .Total}}

<p>Dear {{$customerName}},</p>
<p>Your total is ${{$total}}</p>

Comments

Comments are invisible in the output:

{{/* This comment won't appear in the PDF */}}
{{/*
  Multi-line comments work too.
  Great for documenting complex template logic.
*/}}

Conditional Logic

<!-- Simple if/else -->
{{if .IsVIP}}
  <span class="badge vip">VIP Customer</span>
{{else}}
  <span class="badge">Standard</span>
{{end}}

<!-- Else if -->
{{if eq .Status "paid"}}
  <span style="color: green;">PAID</span>
{{else if eq .Status "overdue"}}
  <span style="color: red;">OVERDUE</span>
{{else}}
  <span style="color: orange;">PENDING</span>
{{end}}
<!-- Combining conditions -->
{{if and .IsVIP (gt .TotalSpent 10000)}}
  <p>Thank you for being a valued
     VIP customer!</p>
{{end}}
Syntax: Functions go before args: {{eq .A .B}}. Nest with parens: {{and (gt .X 5) (lt .X 10)}}
Important: Every {{if}} must have a matching {{end}}. Missing {{end}} is the most common syntax error.

With Blocks

{{with}} changes the dot context and handles nil safely:

<!-- Without 'with' — risky if Customer is nil -->
{{.Customer.Name}}
{{.Customer.Email}}

<!-- With 'with' — safe and cleaner -->
{{with .Customer}}
    <p>Name: {{.Name}}</p>
    <p>Email: {{.Email}}</p>
    {{with .Address}}
        <p>City: {{.City}}</p>
    {{end}}
{{else}}
    <p>No customer data available</p>
{{end}}
Key insight: Inside a {{with}} block, . (dot) refers to the value after with, not the root data. Use $ to access the root: {{$.CompanyName}}

Loops with Range

Basic Loop

{{range .Items}}
  <tr>
    <td>{{.Description}}</td>
    <td>${{printf "%.2f" .UnitPrice}}</td>
  </tr>
{{end}}

With Index

{{range $i, $item := .Items}}
  <td>{{add $i 1}}</td>
  <td>{{$item.Description}}</td>
{{end}}

Empty Fallback

{{range .Items}}
    <p>{{.Name}}</p>
{{else}}
    <p>No items found</p>
{{end}}
Inside range: . (dot) becomes the current item. Use $index for a 0-based counter, add $index 1 for 1-based.

Built-in Comparison Functions

Comparison

{{eq .Status "active"}}  <!-- equal -->
{{ne .Count 0}}          <!-- not equal -->
{{gt .Age 18}}           <!-- greater than -->
{{ge .Score 90}}         <!-- greater or equal -->
{{lt .Price 100}}        <!-- less than -->
{{le .Quantity 0}}       <!-- less or equal -->

Logic

<!-- AND -->
{{if and .IsActive .IsVerified}}

<!-- OR -->
{{if or .IsAdmin .IsModerator}}

<!-- NOT -->
{{if not .IsDeleted}}

<!-- Combined -->
{{if and (gt .Total 0) (not .IsCancelled)}}
Syntax note: Functions go before their arguments: {{eq .A .B}} not {{.A eq .B}}. When nesting, use parentheses: {{and (gt .X 5) (lt .X 10)}}

String & Math Functions

String Functions

{{printf "Hello %s" .Name}}
{{printf "$%.2f" .Price}}
{{printf "%05d" .OrderNum}}
{{upper .Status}}
{{lower .Email}}
{{title .Name}}
{{trim .Input}}

Math Functions

{{add .Price .Tax}}
{{sub .Total .Discount}}
{{mul .Quantity .UnitPrice}}
{{div .Total .Count}}
{{mod .Index 2}}

Practical Example: Calculated Totals

<p>Subtotal: ${{printf "%.2f" .Subtotal}}</p>
<p>Tax ({{printf "%.1f" .TaxRate}}%): ${{printf "%.2f" .TaxAmount}}</p>
<p><strong>Total: ${{printf "%.2f" .Total}}</strong></p>

Custom Functions

Date Functions

<!-- Format a date string -->
{{dateFormat .InvoiceDate "01/02/2006"}}
{{dateFormat .DueDate "January 2, 2006"}}
{{dateFormat .CreatedAt "2006-01-02"}}

<!-- Date reference format:
  01 = month, 02 = day, 06 = year
  15 = hour, 04 = minute, 05 = second
-->

Utility Functions

<!-- Check if field exists -->
{{if isSet .Discount}}
  Discount: {{.Discount}}
{{end}}

<!-- Check non-empty -->
{{if notEmpty .Notes}}
  <p>{{.Notes}}</p>
{{end}}

<!-- Type conversion -->
{{toString .NumericField}}
Go date format quirk: Go uses a reference date (Jan 2, 2006 15:04:05) instead of YYYY-MM-DD tokens. "01/02/2006" means MM/DD/YYYY. "2006-01-02" means YYYY-MM-DD.
⏸ Pause the Video

Exercise 1: Display Customer Info

In the template editor, create a template that displays customer information.

Sample data (JSON tab):

{
  "Company": "Acme Corp",
  "Contact": {
    "Name": "Jane Smith",
    "Email": "jane@acme.com",
    "Phone": "555-0123"
  },
  "IsVIP": true,
  "AccountNumber": "ACM-2024-0042"
}
  1. Show the company name in an <h1>
  2. Show the contact name, email, and phone in a list
  3. Add a "VIP Customer" badge that only appears when IsVIP is true
  4. Show the account number
Hint: Use {{if .IsVIP}} for the conditional badge.

Exercise 1: Solution

<h1>{{.Company}}</h1>

{{if .IsVIP}}
    <span style="background: gold; color: #333; padding: 4px 12px;
                 border-radius: 4px; font-weight: bold;">VIP Customer</span>
{{end}}

<h3>Contact Information</h3>
<ul>
    <li><strong>Name:</strong> {{.Contact.Name}}</li>
    <li><strong>Email:</strong> {{.Contact.Email}}</li>
    <li><strong>Phone:</strong> {{.Contact.Phone}}</li>
</ul>

<p>Account #: {{.AccountNumber}}</p>

What's happening here

  • {{.Company}} accesses a top-level field from the JSON data
  • {{.Contact.Name}} accesses a nested field
  • {{if .IsVIP}} conditionally shows the badge
⏸ Pause the Video

Exercise 2: Line Items Table

Create a table of order line items using {{range}}.

Sample data (JSON tab):

{
  "OrderNumber": "ORD-2024-1234",
  "Items": [
    {
      "SKU": "WDG-001",
      "Description": "Industrial Widget",
      "Quantity": 10,
      "UnitPrice": 24.99
    },
    {
      "SKU": "GDG-002",
      "Description": "Premium Gadget",
      "Quantity": 0,
      "UnitPrice": 49.99
    },
    {
      "SKU": "SPR-003",
      "Description": "Steel Sprocket",
      "Quantity": 25,
      "UnitPrice": 12.50
    }
  ]
}
  1. Create an HTML table with columns: #, SKU, Description, Qty, Unit Price
  2. Use {{range $i, $item := .Items}} to loop and show row numbers
  3. Show "Out of Stock" in red when Quantity is 0
  4. Format prices with 2 decimal places

Exercise 2: Solution

<h2>Order {{.OrderNumber}}</h2>
<table>
  <thead><tr>
    <th>#</th><th>SKU</th><th>Description</th><th>Qty</th><th>Unit Price</th>
  </tr></thead>
  <tbody>
  {{range $i, $item := .Items}}
    <tr>
      <td>{{add $i 1}}</td>
      <td>{{$item.SKU}}</td>
      <td>{{$item.Description}}</td>
      <td>{{if eq $item.Quantity 0}}<span style="color:red;">Out of Stock</span>{{else}}{{$item.Quantity}}{{end}}</td>
      <td>${{printf "%.2f" $item.UnitPrice}}</td>
    </tr>
  {{end}}
  </tbody>
</table>

Section 3

The Template Editor

Your workspace for building documents

Opening the Editor

  1. Navigate to DataMagik → Document Designer
  2. Click "New Template" to create a template
  3. Choose a name, category, and template type:
    • Document (Body) — standard PDF document
    • Header — reusable header template
    • Footer — reusable footer template
  4. Click a template name to open the Template Editor
Categories: Organize templates into categories like "Invoices", "Labels", "Reports" for easy navigation.

The Multi-Tab Interface

TabPurposeLanguage
HTMLTemplate body — HTML with Go template syntaxHTML
CSSStylesheet — styling for the documentCSS
JSONSample Data — test data for previewJSON
SettingsPage size, margins, headers, footers, TTL
DocsBuilt-in reference for all template functions
ZPLLabel templates (ZPL mode only)ZPL
ExportCustom export config (export mode only)

The editor uses Monaco (same as VS Code) with syntax highlighting and autocomplete.

Template Modes

Document Mode

Standard PDF documents:

  • Invoices, reports, packing slips
  • A4 / Letter / custom sizes
  • Headers and footers
  • Multi-page with page breaks

Label Mode

Product and shipping labels:

  • Fixed dimensions (mm)
  • ZPL for Zebra printers
  • Barcodes and QR codes
  • Direct print support

Custom Export Mode

Non-PDF output: CSV, Excel, XML, JSON, Oracle ASN, custom text formats. Configure column mappings and field discovery.

Sample Data (JSON Tab)

The JSON tab provides test data for previewing your template:

{
  "CompanyName": "Acme Corp",
  "InvoiceNumber": "INV-2024-0042",
  "InvoiceDate": "2024-03-15",
  "Customer": {
    "Name": "Jane Smith",
    "Address": "123 Main St, Portland, OR 97201"
  },
  "Items": [
    {
      "Description": "Widget",
      "Quantity": 10,
      "UnitPrice": 24.99
    },
    {
      "Description": "Gadget",
      "Quantity": 5,
      "UnitPrice": 49.99
    }
  ],
  "Subtotal": 499.85,
  "TaxRate": 8.5,
  "TaxAmount": 42.49,
  "Total": 542.34
}
Pro tip: Your JSON field names must exactly match what you use in {{ }}. {{.InvoiceNumber}} requires a JSON key called "InvoiceNumber" (case-sensitive).

Preview & Drafts

Live Preview

  • Click Preview to render your template with sample data
  • Preview uses your HTML + CSS + JSON data to show the result
  • PDF preview renders through ChromeDP (same engine as real generation)

Draft Auto-Save

  • Your work is automatically saved as a draft as you type
  • Drafts persist even if you close the browser
  • Drafts are per-user — they don't affect other users
  • When ready, commit your draft to create a version
Workflow: Edit freely → Preview to check → Commit when satisfied → Draft is cleared after commit.

The Docs Tab

The Docs tab contains a built-in reference for everything you can use in templates:

  • Go Template Syntax — variables, conditionals, loops, functions
  • Barcode Generation — all barcode types with code examples
  • Chart Generation — chart types, data formats, styling
  • Examples & Use Cases — complete invoice, label, report templates
  • Troubleshooting — common errors and fixes
Quick access: Open the Docs tab anytime you need to look up a function name, check barcode syntax, or see a code example.
⏸ Pause the Video

Exercise 3: Create Your First Template

Navigate the Document Designer and create your first template:

  1. Go to DataMagik → Document Designer
  2. Click New Template, name it "My First Invoice"
  3. In the HTML tab, write a simple heading: <h1>Hello, Document Designer!</h1>
  4. Click Preview to see it render
  5. Switch to the CSS tab, JSON tab, and Docs tab to explore
  6. Open the Docs tab and find the barcode section
Goal: Get comfortable navigating the editor tabs. We'll build on this template in later exercises.

Exercise 3: What You Found

You should now be familiar with:

  • The template list page — browsing and creating templates
  • The HTML tab — Monaco editor with syntax highlighting
  • The CSS tab — separate stylesheet editor
  • The JSON tab — sample data for preview
  • The Settings tab — page size, margins, headers/footers
  • The Docs tab — built-in function reference
  • The Preview button — renders your template live
Next: We'll use the JSON tab to add sample data and make our template dynamic.
⏸ Pause the Video

Exercise 4: Build Invoice Sample Data

Design a complete JSON data structure for an invoice and use it in your template:

  1. In the JSON tab, create a structure with:
    • Company info: name, address, phone
    • Customer: name, address (street, city, state, zip)
    • Invoice: number, date, due date, payment terms
    • Line items array: description, quantity, unit price
    • Subtotal, tax rate, tax amount, total
  2. In the HTML tab, reference at least 5 fields
  3. Use {{range}} to display the line items
  4. Preview to verify it renders correctly

Exercise 4: Solution

JSON (Sample Data)

{
  "CompanyName": "DataMagik Inc.",
  "CompanyAddress": "456 Tech Ave",
  "InvoiceNumber": "INV-2024-0042",
  "InvoiceDate": "2024-03-15",
  "DueDate": "2024-04-14",
  "Customer": {
    "Name": "Jane Smith",
    "Street": "123 Main St",
    "City": "Portland",
    "State": "OR",
    "Zip": "97201"
  },
  "Items": [
    {
      "Description": "Widget A",
      "Quantity": 10,
      "UnitPrice": 24.99
    },
    {
      "Description": "Gadget B",
      "Quantity": 5,
      "UnitPrice": 49.99
    }
  ],
  "Subtotal": 299.85,
  "TaxRate": 8.5,
  "Total": 325.34
}

HTML (Template)

<h1>{{.CompanyName}}</h1>
<p>{{.CompanyAddress}}</p>
<h2>Invoice #{{.InvoiceNumber}}</h2>
<p>Date: {{.InvoiceDate}} | Due: {{.DueDate}}</p>
<h3>Bill To:</h3>
<p>{{.Customer.Name}}</p>
<p>{{.Customer.Street}}</p>
<p>{{.Customer.City}}, {{.Customer.State}} {{.Customer.Zip}}</p>
<table>
  <tr><th>Description</th><th>Qty</th><th>Price</th></tr>
  {{range .Items}}
  <tr><td>{{.Description}}</td><td>{{.Quantity}}</td>
    <td>${{printf "%.2f" .UnitPrice}}</td></tr>
  {{end}}
</table>
<p><strong>Total: ${{printf "%.2f" .Total}}</strong></p>

Section 4

CSS Styling for Documents

Making your documents look professional

CSS Tab vs Inline Styles

CSS Tab (Recommended)

/* In the CSS tab */
.invoice-header {
    display: flex;
    justify-content: space-between;
    border-bottom: 2px solid #333;
    padding-bottom: 20px;
    margin-bottom: 30px;
}
.items-table th {
    background: #f0f0f0;
    padding: 10px;
    text-align: left;
}

Reusable, clean, easy to maintain

Inline Styles

<!-- In the HTML tab -->
<div style="display: flex;
    justify-content: space-between;
    border-bottom: 2px solid #333;
    padding-bottom: 20px;">
    ...
</div>

Quick for one-offs, gets messy fast

Best practice: Use the CSS tab for all styling. It keeps your HTML clean and makes changes easier. Use inline styles only for dynamic values like style="color: {{if eq .Status "overdue"}}red{{else}}green{{end}}"

CSS Presets Library

The editor includes a CSS Presets library with ready-to-use styles:

  • Professional invoice layouts
  • Report styles with headers and footers
  • Table formatting presets
  • Typography and color schemes

Apply a preset to get started quickly, then customize to your needs.

How to use: Click the CSS Presets button in the CSS tab toolbar, browse categories, and click to apply. You can combine multiple presets.

Document Layout Patterns

Flexbox Header

.invoice-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 30px;
}
.company-info { text-align: left; }
.invoice-info { text-align: right; }

Table Styling

.items-table {
    width: 100%;
    border-collapse: collapse;
    margin: 20px 0;
}
.items-table th {
    background: #2d3748;
    color: white;
    padding: 10px 12px;
    text-align: left;
}
.items-table td {
    padding: 8px 12px;
    border-bottom: 1px solid #e2e8f0;
}
.items-table tr:nth-child(even) {
    background: #f7fafc;
}

Page Breaks & Print Styling

CSS Classes

.section-break {
  page-break-before: always;
}
.keep-together {
  page-break-inside: avoid;
}
@media print {
  body { font-size: 11pt; color: #000; }
  .no-print { display: none; }
}

Usage in HTML

<div class="section">
  <h2>Section 1: Summary</h2>
</div>

<div class="section section-break">
  <h2>Section 2: Details</h2>
  <!-- starts on a new page -->
</div>
Key properties: page-break-before: always forces new page. page-break-inside: avoid keeps elements together (great for table rows).

Typography for Print

body {
    font-family: 'Helvetica Neue', Arial, sans-serif;
    font-size: 10pt;    /* pt not px for print */
    line-height: 1.5;
    color: #1a202c;
    margin: 0; padding: 40px;
}
h1 { font-size: 20pt; color: #2d3748; margin-bottom: 5px; }
h2 { font-size: 14pt; color: #4a5568; margin-top: 25px; }
h3 { font-size: 12pt; color: #4a5568; }
.totals { text-align: right; margin-top: 20px; }
.totals .grand-total {
    font-size: 14pt; font-weight: bold;
    border-top: 2px solid #2d3748; padding-top: 8px;
}
Use pt not px: Points (pt) are a print unit and render consistently across devices. 10pt is standard for document body text, 8pt for fine print.
⏸ Pause the Video

Exercise 5: Style Your Invoice

Add professional CSS styling to your invoice template from Exercise 4:

  1. In the CSS tab, add styles for:
    • A flexbox header with company info (left) and invoice details (right)
    • A styled "Bill To" section with a light background
    • A table with header background, borders, and alternating row colors
    • Right-aligned totals section with the grand total emphasized
  2. Update your HTML tab to use CSS classes instead of inline styles
  3. Preview the result
Hint: Start with .invoice-header { display: flex; justify-content: space-between; }

Exercise 5: Solution

CSS Tab

body { font-family: Arial, sans-serif; font-size: 10pt; color: #333; padding: 30px; }
.header { display: flex; justify-content: space-between; border-bottom: 2px solid #2d3748; padding-bottom: 15px; margin-bottom: 20px; }
.bill-to { background: #f7fafc; padding: 15px; border-radius: 6px; margin: 15px 0; }
.items-table { width: 100%; border-collapse: collapse; margin: 20px 0; }
.items-table th { background: #2d3748; color: white; padding: 10px; text-align: left; }
.items-table td { padding: 8px 10px; border-bottom: 1px solid #e2e8f0; }
.items-table tr:nth-child(even) { background: #f7fafc; }
.totals { text-align: right; margin-top: 15px; }
.totals p { margin: 4px 0; }
.grand-total { font-size: 14pt; font-weight: bold; border-top: 2px solid #2d3748; padding-top: 8px; }

HTML Tab

<div class="header">
  <div>
    <h1>{{.CompanyName}}</h1>
    <p>{{.CompanyAddress}}</p>
    <p>{{.CompanyPhone}}</p>
  </div>
  <div style="text-align:right;">
    <h2>INVOICE</h2>
    <p>#{{.InvoiceNumber}}</p>
    <p>Date: {{.InvoiceDate}}</p>
    <p>Due: {{.DueDate}}</p>
  </div>
</div>
<div class="bill-to">
  <h3>Bill To:</h3>
  <p>{{.Customer.Name}}</p>
  <p>{{.Customer.Street}}</p>
  <p>{{.Customer.City}}, {{.Customer.State}} {{.Customer.Zip}}</p>
</div>
⏸ Pause the Video

Exercise 6: Multi-Page Report

Create a report template with multiple sections on separate pages:

  1. Create a new template called "Monthly Report"
  2. Add three sections: Summary, Details, Appendix
  3. Use page-break-before: always to put each section on its own page
  4. Add a footer with the current date
  5. In the Summary, display 3 KPI values from your sample data
  6. In the Details, use {{range}} for a data table
  7. Preview and verify each section starts on a new page
Hint: Use CSS class .section-break { page-break-before: always; } on the 2nd and 3rd section divs.

Exercise 6: Solution

HTML

<div class="section">
  <h1>Monthly Sales Report</h1>
  <div class="kpi-row">
    <div class="kpi"><h3>${{printf "%.0f" .TotalRevenue}}</h3><p>Revenue</p></div>
    <div class="kpi"><h3>{{.TotalOrders}}</h3><p>Orders</p></div>
  </div>
</div>
<div class="section section-break">
  <h2>Sales Details</h2>
  <table>{{range .Products}}
    <tr><td>{{.Name}}</td><td>${{printf "%.2f" .Revenue}}</td></tr>
  {{end}}</table>
</div>
<div class="section section-break">
  <h2>Appendix</h2>
</div>

CSS

.section-break {
  page-break-before: always;
}
.kpi-row {
  display: flex;
  gap: 20px; margin: 20px 0;
}
.kpi {
  background: #f7fafc;
  padding: 20px;
  border-radius: 8px;
  text-align: center; flex: 1;
}

Section 5

Document Settings & Generation

Configuring output and understanding the pipeline

Page Size & Orientation

SettingOptionsUse Case
Page SizeLetter (8.5"×11"), A4 (210×297mm), CustomLetter for US, A4 for international
OrientationPortrait, LandscapeLandscape for wide tables/dashboards
Custom SizeWidth × Height in mmLabels, tickets, custom forms
Quick start: Most invoices and reports use Letter, Portrait. Dashboards with charts often work better in Landscape.

Margins & Headers/Footers

Margins

Set margins for top, right, bottom, left (in points or inches).

Typical values: 20–25mm on all sides for professional documents.

Header/Footer Modes

ModeDescription
Company DefaultsUses company-wide header/footer templates
Template-SpecificSelect a specific header/footer template
InlineWrite header/footer HTML directly in settings
NoneNo header or footer
Page numbers: Add Page {{.PageNumber}} of {{.TotalPages}} in your footer template for automatic page numbering.

TTL & Output Options

Time-to-Live (TTL)

  • Set how long generated documents persist before auto-deletion
  • Common values: 30, 60, 90 days
  • Compliance requirement for some industries

Background & Print Settings

  • Print backgrounds — enable to render CSS background colors in PDF
  • One-time access — document URL works only once

Output Formats

FormatUse Case
PDFInvoices, reports, official documents
HTMLEmail-ready content, web preview
CSV / ExcelData exports, spreadsheet reports
XML / JSONSystem integrations, data exchange

How Document Generation Works

Documents are generated asynchronously — the main app never creates PDFs directly:

1. API Request        POST /api/document-designer/generate
                          ↓
2. Quota Check        QuotaEnforcementService checks limits
                          ↓
3. Queue to Redis     Published to priority queue
                          ↓
4. Return Immediately Response with request_id for polling
                          ↓
5. Worker Processes   Worker dequeues → calls PDF service → uploads to S3
                          ↓
6. Status Check       GET /api/document-designer/status/{request_id}

Priority Queues

  • High — user-initiated, interactive previews
  • Normal — standard generation requests
  • Low — bulk/batch generation

Preview vs Full Generate

Preview

  • Uses sample data from JSON tab
  • Quick rendering for development
  • Not stored permanently
  • No S3 upload

Full Generate

  • Uses real data from API/scripts
  • Queued through Redis pipeline
  • Stored in S3 with TTL
  • Tracked in generated documents list
  • Counts against quota
Important: Preview uses your JSON tab data. Full generation uses data passed through the API or script engine. Make sure your template works with both.
⏸ Pause the Video

Exercise 7: Configure Invoice Settings

Configure your invoice template's document settings:

  1. Open your invoice template's Settings tab
  2. Set page size to Letter, orientation to Portrait
  3. Set margins: top 25mm, right 20mm, bottom 25mm, left 20mm
  4. Set header mode to Inline and add your company name
  5. Set footer mode to Inline and add: Page {{.PageNumber}} of {{.TotalPages}}
  6. Enable Print backgrounds
  7. Set TTL to 90 days
  8. Preview and verify header/footer appear on the output

Exercise 7: Solution

In the Settings tab, you should have configured:

SettingValue
Page SizeLetter (8.5" × 11")
OrientationPortrait
Top Margin25mm
Right Margin20mm
Bottom Margin25mm
Left Margin20mm
Header ModeInline
Footer ModeInline
Print BackgroundsEnabled
TTL90 days
Note: If your company has set up default headers/footers, you can switch to "Company Defaults" mode instead of writing inline headers. Check with your admin.

Section 6

Barcodes & QR Codes

Scannable codes for documents and labels

1D Barcodes

Code 128 (Most Common)

<!-- Renders an <img> tag -->
{{code128 .SKU 200 50}}

General purpose, alphanumeric. Best for SKUs, order numbers, serial numbers.

Other 1D Formats

{{code39 .InventoryCode 200 50}}
{{ean13 .EANCode 200 80}}
{{pdf417 .DocumentInfo 300 80}}

Code 39 for inventory, EAN-13 for products, PDF417 for dense data.

How it works: Barcode functions return a complete <img> tag with a base64-encoded PNG. Just use {{code128 .Data 200 50}} — no need to write the img tag yourself.

QR Codes & 2D Barcodes

<!-- QR Code (takes data and size) -->
{{qrcode .PaymentURL 150}}

<!-- Data Matrix (compact, good error correction) -->
{{datamatrix .SerialData 80}}

<!-- Generic barcode function -->
{{barcode "qr" .URL 150 150}}

Common QR Code Uses

  • Payment links: {{qrcode .PaymentURL 150}}
  • Tracking URLs: {{qrcode .TrackingURL 120}}
  • Contact info (vCard): {{qrcode .VCardData 150}}
  • Product page: {{qrcode .ProductURL 100}}
QR size tip: 100–150px is ideal for documents. Larger for easy scanning from distance, smaller for dense labels.

Barcode Function Reference

FunctionParametersReturns
code128data, width, heightHTML img tag
code39data, width, heightHTML img tag
qrcodedata, sizeHTML img tag
ean13data, width, heightHTML img tag
datamatrixdata, sizeHTML img tag
pdf417data, width, heightHTML img tag
barcodetype, data, width, heightHTML img tag

Base64 Variants

Add _base64 suffix for raw base64 (manual img tags):

<!-- Base64 variant — you build the img tag yourself -->
<img src="data:image/png;base64,{{code128_base64 .SKU 200 50}}" alt="Barcode" style="display:block;">

Styling Barcodes

<!-- Centered barcode with label -->
<div style="text-align: center; margin: 20px 0;">
    {{code128 .InvoiceNumber 250 60}}
    <p style="font-family: monospace; font-size: 10pt; margin-top: 5px;">
        {{.InvoiceNumber}}
    </p>
</div>

<!-- Barcode in header (right-aligned) -->
<div class="header">
    <div class="company-info">
        <h1>{{.CompanyName}}</h1>
    </div>
    <div style="text-align: right;">
        {{code128 .InvoiceNumber 200 50}}
        <p>Invoice #{{.InvoiceNumber}}</p>
    </div>
</div>
Validate data first: Always check that barcode data exists before rendering. An empty value may cause an error: {{if .SKU}}{{code128 .SKU 200 50}}{{end}}
⏸ Pause the Video

Exercise 8: Add Barcodes to Your Invoice

Enhance your invoice template with barcodes:

  1. Add a Code 128 barcode for the invoice number in the top-right of the header
  2. Add the invoice number as text below the barcode
  3. Add a QR code at the bottom linking to a payment URL
  4. Add the payment URL field to your sample JSON: "PaymentURL": "https://pay.example.com/inv/0042"
  5. Guard both barcodes with {{if}} checks
  6. Preview and verify both barcodes render
Hint: {{code128 .InvoiceNumber 200 50}} for the barcode, {{qrcode .PaymentURL 120}} for the QR code.

Exercise 8: Solution

<!-- In the header div, right side -->
<div style="text-align: right;">
    <h2>INVOICE</h2>
    {{if .InvoiceNumber}}
        {{code128 .InvoiceNumber 200 50}}
        <p style="font-family: monospace; font-size: 9pt;">#{{.InvoiceNumber}}</p>
    {{end}}
    <p>Date: {{.InvoiceDate}}</p>
    <p>Due: {{.DueDate}}</p>
</div>

<!-- ... items table and totals ... -->

<!-- Payment QR code at the bottom -->
{{if .PaymentURL}}
<div style="text-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid #e2e8f0;">
    <p style="font-size: 9pt; color: #666;">Scan to pay online:</p>
    {{qrcode .PaymentURL 120}}
    <p style="font-size: 8pt; color: #999;">{{.PaymentURL}}</p>
</div>
{{end}}
⏸ Pause the Video

Exercise 9: Product Label

Create a product label template:

  1. Create a new template called "Product Label"
  2. Add sample data with product info: name, SKU, EAN code, price, product URL
  3. Include an EAN-13 barcode for the product EAN: {{ean13 .EANCode 180 70}}
  4. Include a QR code linking to the product page
  5. Layout: product name at top, EAN barcode centered, QR code and price below
  6. Style it as a compact label (border, centered text, monospace for SKU)
{
  "Name": "Industrial Widget",
  "SKU": "WDG-001",
  "EANCode": "5901234123457",
  "Price": 24.99,
  "ProductURL": "https://shop.example.com/p/WDG-001"
}

Exercise 9: Solution

<div style="width: 300px; border: 2px solid #000; padding: 15px; text-align: center; font-family: Arial, sans-serif;">
    <h2 style="margin: 0 0 10px; font-size: 14pt;">{{.Name}}</h2>
    <p style="font-family: monospace; color: #666; margin: 5px 0;">SKU: {{.SKU}}</p>

    <div style="margin: 15px 0;">
        {{if .EANCode}}
            {{ean13 .EANCode 180 70}}
            <p style="font-family: monospace; font-size: 9pt;">{{.EANCode}}</p>
        {{end}}
    </div>

    <div style="display: flex; justify-content: space-between; align-items: center;">
        <div>
            {{if .ProductURL}}{{qrcode .ProductURL 80}}{{end}}
        </div>
        <div style="font-size: 18pt; font-weight: bold; color: #e53e3e;">
            ${{printf "%.2f" .Price}}
        </div>
    </div>
</div>

Section 7

Charts & Data Visualization

Line, bar, pie, and doughnut charts for reports

Chart Types

Line & Bar Charts

<!-- Line chart -->
{{lineChart .SalesData "sales" 800 400}}

<!-- Bar chart -->
{{barChart .CategoryData "cats" 800 400}}

<!-- Generic function -->
{{chart "line" .Data "id" 800 400}}
{{chart "bar" .Data "id" 800 400}}

Use line for trends over time, bar for comparisons.

Pie & Doughnut Charts

<!-- Pie chart -->
{{pieChart .Distribution "dist" 600 600}}

<!-- Doughnut chart -->
{{doughnutChart .Distribution "dnut" 600 600}}

Use pie/doughnut for part-to-whole relationships.

Parameters: All chart functions take: (data, elementID, width, height). The elementID must be unique per chart on the page.

Chart Data Formats

Object Format (Chart.js structure)

{
  "SalesData": {
    "labels": ["January", "February", "March", "April"],
    "datasets": [{
      "label": "Revenue",
      "data": [12000, 15000, 13500, 18000]
    }]
  }
}
{{lineChart .SalesData "salesChart" 800 400}}

CSV Format (simple label,value pairs)

<!-- No JSON data needed — data is inline -->
{{chart_line "Monthly Sales" "Jan,12000|Feb,15000|Mar,13500|Apr,18000" 800 400}}
{{chart_bar "By Category" "Widgets,45|Gadgets,30|Sprockets,25" 800 400}}
{{chart_pie "Market Share" "Product A,45|Product B,35|Product C,20" 600 600}}
CSV format: Great for quick charts without complex JSON. Format: "label1,value1|label2,value2|..."

Chart Function Reference

FunctionParametersData Format
charttype, data, elementID, width, heightObject
lineChartdata, elementID, width, heightObject
barChartdata, elementID, width, heightObject
pieChartdata, elementID, width, heightObject
doughnutChartdata, elementID, width, heightObject
chart_linetitle, csvData, width, heightCSV
chart_bartitle, csvData, width, heightCSV
chart_pietitle, csvData, width, heightCSV

All functions also have _base64 variants (e.g., chart_line_base64) that return base64-encoded images.

Dashboard Layout with Charts

<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
    <div class="chart-card">
        <h3>Revenue Trend</h3>
        {{lineChart .RevenueTrend "revChart" 400 250}}
    </div>
    <div class="chart-card">
        <h3>Sales by Category</h3>
        {{pieChart .CategorySales "catChart" 400 250}}
    </div>
    <div class="chart-card" style="grid-column: 1 / -1;">
        <h3>Monthly Comparison</h3>
        {{barChart .MonthlyData "monthChart" 800 300}}
    </div>
</div>
.chart-card {
    background: #f9f9f9;
    border-radius: 8px;
    padding: 15px;
    border: 1px solid #e0e0e0;
}
.chart-card h3 { margin-top: 0; }
⏸ Pause the Video

Exercise 10: Sales Report with Charts

Create a sales report template with charts:

  1. Create a new template called "Monthly Sales Report"
  2. Add a bar chart showing revenue by month using CSV format: {{chart_bar "Monthly Revenue" "Jan,12000|Feb,15000|Mar,13500|Apr,18000|May,21000|Jun,19500" 800 400}}
  3. Add a pie chart showing revenue by category: {{chart_pie "By Category" "Widgets,45|Gadgets,30|Sprockets,25" 600 400}}
  4. Add a summary table below the charts with top products
  5. Style with a professional layout (title, date, charts in grid, table below)

Exercise 10: Solution

<h1>Monthly Sales Report</h1>
<p>Report Period: January – June 2024</p>

<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin: 20px 0;">
    <div class="chart-card">
        <h3>Revenue by Month</h3>
        {{chart_bar "Monthly Revenue" "Jan,12000|Feb,15000|Mar,13500|Apr,18000|May,21000|Jun,19500" 400 300}}
    </div>
    <div class="chart-card">
        <h3>Revenue by Category</h3>
        {{chart_pie "By Category" "Widgets,45|Gadgets,30|Sprockets,25" 400 300}}
    </div>
</div>

<h3>Top Products</h3>
<table class="items-table">
    <tr><th>Product</th><th>Units Sold</th><th>Revenue</th></tr>
    {{range .TopProducts}}
    <tr><td>{{.Name}}</td><td>{{.Units}}</td><td>${{printf "%.2f" .Revenue}}</td></tr>
    {{end}}
</table>
⏸ Pause the Video

Exercise 11: KPI Dashboard

Build a one-page dashboard with KPIs and charts:

  1. Create 4 KPI boxes in a row (Total Revenue, Orders, Avg Order Value, Return Rate)
  2. Use sample data JSON for the KPI values
  3. Add a line chart using object format (define labels + datasets in JSON)
  4. Add a doughnut chart for category distribution
  5. Use landscape orientation in Settings for more horizontal space
  6. Style KPI boxes with large numbers and subtle backgrounds
{
  "TotalRevenue": 125000,
  "TotalOrders": 842,
  "AvgOrderValue": 148.46,
  "ReturnRate": 3.2,
  "DailyOrders": {
    "labels": ["Mon", "Tue", "Wed", "Thu", "Fri"],
    "datasets": [{
      "label": "Orders",
      "data": [120, 145, 132, 168, 155]
    }]
  }
}

Exercise 11: Solution

<h1>Sales Dashboard</h1>

<div style="display: flex; gap: 15px; margin: 20px 0;">
    <div class="kpi"><h2>${{printf "%.0f" .TotalRevenue}}</h2><p>Total Revenue</p></div>
    <div class="kpi"><h2>{{.TotalOrders}}</h2><p>Total Orders</p></div>
    <div class="kpi"><h2>${{printf "%.2f" .AvgOrderValue}}</h2><p>Avg Order Value</p></div>
    <div class="kpi"><h2>{{printf "%.1f" .ReturnRate}}%</h2><p>Return Rate</p></div>
</div>

<div style="display: flex; gap: 20px;">
    <div style="flex: 2;">
        <h3>Daily Orders</h3>
        {{lineChart .DailyOrders "dailyChart" 600 300}}
    </div>
    <div style="flex: 1;">
        <h3>By Category</h3>
        {{chart_pie "Categories" "Widgets,40|Gadgets,35|Sprockets,25" 300 300}}
    </div>
</div>
.kpi { flex: 1; background: #f0f4f8; padding: 15px; border-radius: 8px; text-align: center; }
.kpi h2 { margin: 0; color: #2d3748; font-size: 24pt; }
.kpi p { margin: 5px 0 0; color: #718096; font-size: 10pt; }

Section 8

Version Control & Branching

Track changes, collaborate safely, rollback when needed

Why Version Control?

  • Safety net — rollback to any previous version if something breaks
  • Collaboration — multiple people can work on branches without conflicts
  • Audit trail — who changed what and when
  • Experimentation — try changes on a branch without affecting production
Think of it like Git: The Document Designer has branches, commits, merges, and diffs — the same concepts as Git, but built into the UI. No command line needed.

Branches & Commits

The Main Branch

  • Every template has a main branch (the production version)
  • Main is protected — changes go through branches or approvals
  • Document generation uses the main branch by default

Working with Branches

1. Create branch    →   "add-discount-column"
2. Edit template    →   make changes on the branch
3. Preview          →   verify changes look correct
4. Commit           →   save with a descriptive message
5. Merge to main    →   promote to production

Commit Messages

Write clear messages describing what changed and why:

  • "Add discount column to line items table"
  • "Update header to new company branding"
  • "Fix tax calculation display for zero-tax orders"

Version History, Diff & Rollback

Version History

  • View all commits on a branch with timestamps and authors
  • Click any version to see its content

Diff / Changelog

  • Compare any two versions side by side
  • See exactly what changed in HTML and CSS
  • Review before merging or approving

Rollback

  • Revert to any previous version instantly
  • Creates a new commit (doesn't delete history)
  • Useful when a change breaks something in production
Rollback is safe: It creates a new version that copies the old content. No history is lost.
⏸ Pause the Video

Exercise 12: Branch, Edit, Merge

Practice the full version control workflow:

  1. Open your invoice template
  2. Create a branch called "add-payment-terms"
  3. Add a payment terms section below the totals:
    <div class="payment-terms">
        <h3>Payment Terms</h3>
        <p>{{.PaymentTerms}}</p>
        <p>Please make payment by {{.DueDate}}</p>
    </div>
  4. Commit with message: "Add payment terms section"
  5. View the changelog/diff to see your changes
  6. Merge the branch back to main
  7. Verify the change appears on the main branch

Exercise 12: Walkthrough

Steps completed:

  1. Created branch "add-payment-terms" from main
  2. Added payment terms HTML with {{.PaymentTerms}} and {{.DueDate}}
  3. Committed with a descriptive message
  4. Reviewed diff — shows the new <div class="payment-terms"> block
  5. Merged branch to main — changes now live
Best practice: Always review the diff before merging. It's your last chance to catch mistakes before the change goes to production.

Section 9

Approval Workflows

Quality control for template changes

When to Use Approvals

  • Regulatory compliance — changes to legal documents need sign-off
  • Brand consistency — marketing-approved templates
  • Quality control — second pair of eyes before production
  • Team workflows — junior designers submit, seniors approve

The Approval Flow

1. Author commits a version
2. Author submits an approval request
3. Reviewer receives notification
4. Reviewer views the diff and tests the template
5. Reviewer approves or rejects (with comments)
6. If approved, version becomes active on main

Requesting & Reviewing Approvals

Requesting Approval

  1. Commit your changes on a branch
  2. Click "Request Approval" on the version
  3. The request appears in the Approvals tab

Reviewing as an Approver

  1. Open the template's Approvals tab
  2. Review the diff between the current version and the proposed change
  3. Click Approve or Reject
  4. Add comments explaining your decision

Withdrawing a Request

Found an issue? Click "Withdraw" to cancel the approval request, make fixes, and re-submit.

⏸ Pause the Video

Exercise 13: Approval Workflow

Practice the approval process (pair with a colleague if possible):

  1. Create a branch on your invoice template
  2. Make a visible change (e.g., add a "Thank you for your business!" footer)
  3. Commit the change
  4. Submit an approval request
  5. If pairing: have your colleague review and approve
  6. If solo: navigate to the Approvals tab and approve your own request
  7. Verify the approved version is now active

Exercise 13: Walkthrough

  • Created branch, made change, committed
  • Submitted approval request from the version history
  • Reviewed the diff showing the new footer text
  • Approved with comment: "Looks good, approved"
  • Version promoted to active on main
Tip: In production, set up approval requirements so that certain templates require approval before any version can be activated. This enforces the workflow.

Section 10

Script Engine Integration

Generate documents programmatically from scripts

The documents API

The Script Engine provides two functions for document generation:

Synchronous

// Wait for the PDF to be ready
const result = documents.generateSync(
    "My Invoice Template",
    {
        InvoiceNumber: "INV-2024-0042",
        Customer: { Name: "Jane Smith" },
        Items: [
            { Description: "Widget", Quantity: 10, UnitPrice: 24.99 }
        ],
        Total: 249.90
    }
);

console.log(result.url);  // PDF URL

Asynchronous

// Fire and forget
const requestId = documents.generate(
    "My Invoice Template",
    {
        InvoiceNumber: "INV-2024-0042",
        Customer: { Name: "Jane Smith" },
        Total: 249.90
    }
);

// Check status later
console.log("Request ID:", requestId);
Which to use? Use generateSync when you need the PDF URL immediately (e.g., to email it). Use generate for batch/bulk generation where you don't need the URL right away.

Passing Field Values

The second argument is a JavaScript object whose keys become template fields:

// Script Engine
const data = {
    CompanyName: "Acme Corp",           // → {{.CompanyName}}
    Customer: {
        Name: "Jane Smith",             // → {{.Customer.Name}}
        Address: { City: "Portland" }   // → {{.Customer.Address.City}}
    },
    Items: [                            // → {{range .Items}}
        { Description: "Widget", Quantity: 10, UnitPrice: 24.99 }
    ],
    Total: 249.90                       // → {{.Total}}
};

const result = documents.generateSync("My Invoice Template", data);
Key names must match exactly: If your template uses {{.InvoiceNumber}}, you must pass InvoiceNumber (case-sensitive) in the data object.

Generation Options

const result = documents.generateSync("Invoice Template", data, {
    branch: "main",          // Which branch to use (default: "main")
    priority: "high",        // Queue priority: "high", "normal", "low"
    outputFormat: "pdf",     // "pdf", "html", "csv", "excel", "xml", "json"
    returnType: "url"        // "url" (default) or "base64"
});

// Response
console.log(result.url);       // Download URL
console.log(result.documentId); // Document ID for tracking

Common Patterns

// Generate and email
const pdf = documents.generateSync("Invoice", invoiceData);
email.send({
    to: customer.email,
    subject: `Invoice #${invoiceData.InvoiceNumber}`,
    body: `<p>Please find your invoice attached.</p>`,
    attachments: [{ url: pdf.url, filename: "invoice.pdf" }]
});

Scheduling Document Generation

// Schedule: Every Monday at 8:00 AM (cron: 0 8 * * 1)
function main(context) {
    const salesData = http.get("https://api.example.com/sales/weekly");

    const report = documents.generateSync("Weekly Sales Report", {
        ReportDate: new Date().toISOString().split('T')[0],
        TotalRevenue: salesData.totalRevenue,
        TotalOrders: salesData.totalOrders,
        TopProducts: salesData.topProducts
    });

    email.send({
        to: "sales-team@company.com",
        subject: "Weekly Sales Report",
        body: "<p>This week's report is attached.</p>",
        attachments: [{ url: report.url, filename: "weekly-report.pdf" }]
    });
    return { status: "sent", url: report.url };
}
Cron schedule: 0 8 * * 1 = every Monday at 8:00 AM. Set this in the Script Engine's schedule settings.
⏸ Pause the Video

Exercise 14: Script-Generated Invoice

Write a Script Engine script that generates an invoice PDF:

  1. Open the Script Engine
  2. Create a new script called "Generate Test Invoice"
  3. Define an order object with: customer info, 3 line items, and totals
  4. Call documents.generateSync("My First Invoice", orderData)
  5. Log the resulting PDF URL with console.log()
  6. Return the result object
function main(context) {
    const orderData = {
        // Build your data object here...
    };
    // Generate and return the result
}

Exercise 14: Solution

function main(context) {
    const orderData = {
        CompanyName: "DataMagik Inc.",
        CompanyAddress: "456 Tech Ave, Suite 200",
        CompanyPhone: "(555) 867-5309",
        InvoiceNumber: "INV-2024-0042",
        InvoiceDate: "2024-03-15",
        DueDate: "2024-04-14",
        PaymentTerms: "Net 30",
        Customer: {
            Name: "Jane Smith",
            Street: "123 Main St",
            City: "Portland", State: "OR", Zip: "97201"
        },
        Items: [
            { Description: "Widget A", Quantity: 10, UnitPrice: 24.99 },
            { Description: "Gadget B", Quantity: 5, UnitPrice: 49.99 },
            { Description: "Sprocket C", Quantity: 25, UnitPrice: 12.50 }
        ],
        Subtotal: 812.35, TaxRate: 8.5, TaxAmount: 69.05, Total: 881.40,
        PaymentURL: "https://pay.example.com/inv/0042"
    };

    const result = documents.generateSync("My First Invoice", orderData);
    console.log("PDF URL:", result.url);
    return result;
}
⏸ Pause the Video

Exercise 15: Automated Report + Email

Build an automation script that generates a report and emails it:

  1. Create a script called "Monthly Report Emailer"
  2. Build report data with KPIs, a products array, and a report date
  3. Call documents.generateSync("Monthly Sales Report", reportData)
  4. Send the PDF via email using email.send()
  5. Log both the PDF URL and email status
function main(context) {
    // 1. Build report data
    // 2. Generate PDF
    // 3. Email the PDF
    // 4. Log and return results
}
Bonus: If you have access to schedule settings, set this up on a monthly cron: 0 8 1 * * (1st of every month at 8 AM)

Exercise 15: Solution

function main(context) {
    const reportData = {
        ReportDate: new Date().toISOString().split('T')[0],
        TotalRevenue: 125000, TotalOrders: 842, AvgOrderValue: 148.46,
        TopProducts: [
            { Name: "Widget Pro", Units: 245, Revenue: 36750 },
            { Name: "Gadget Plus", Units: 189, Revenue: 28350 },
            { Name: "Sprocket XL", Units: 156, Revenue: 23400 }
        ]
    };
    // Generate the PDF
    const report = documents.generateSync("Monthly Sales Report", reportData);
    console.log("Report generated:", report.url);
    // Email the report
    email.send({
        to: "manager@company.com",
        subject: "Monthly Sales Report - " + reportData.ReportDate,
        body: "<p>The monthly sales report is attached.</p>",
        attachments: [{ url: report.url, filename: "monthly-report.pdf" }]
    });
    return { reportUrl: report.url, emailSent: true };
}

Section 11

Real-World Examples

Complete templates you can use as starting points

Example 1: Complete Invoice

<div class="header">
    <div>
        <h1>{{.CompanyName}}</h1>
        <p>{{.CompanyAddress}}</p>
        <p>{{.CompanyPhone}}</p>
    </div>
    <div style="text-align: right;">
        <h2>INVOICE</h2>
        {{if .InvoiceNumber}}{{code128 .InvoiceNumber 200 50}}{{end}}
        <p>#{{.InvoiceNumber}}</p>
        <p>Date: {{dateFormat .InvoiceDate "01/02/2006"}}</p>
        <p>Due: {{dateFormat .DueDate "01/02/2006"}}</p>
        <p>Terms: {{.PaymentTerms}}</p>
    </div>
</div>

<div class="bill-to">
    <h3>Bill To:</h3>
    {{with .Customer}}
    <p><strong>{{.Name}}</strong></p>
    <p>{{.Street}}</p>
    <p>{{.City}}, {{.State}} {{.Zip}}</p>
    {{end}}
</div>

Example 1: Invoice (continued)

<table class="items-table">
  <thead><tr>
    <th>#</th><th>Description</th><th>Qty</th><th>Unit Price</th><th>Total</th>
  </tr></thead>
  <tbody>
  {{range $i, $item := .Items}}
  <tr>
    <td>{{add $i 1}}</td>
    <td>{{$item.Description}}</td>
    <td>{{$item.Quantity}}</td>
    <td>${{printf "%.2f" $item.UnitPrice}}</td>
    <td>${{printf "%.2f" (mul $item.Quantity $item.UnitPrice)}}</td>
  </tr>
  {{end}}
  </tbody>
</table>
<div class="totals">
  <p>Subtotal: ${{printf "%.2f" .Subtotal}}</p>
  {{if .TaxRate}}<p>Tax ({{printf "%.1f" .TaxRate}}%): ${{printf "%.2f" .TaxAmount}}</p>{{end}}
  <p class="grand-total">Total: ${{printf "%.2f" .Total}}</p>
</div>
{{if .PaymentURL}}
<div style="text-align:center; margin-top:20px; border-top:1px solid #ccc; padding-top:10px;">
  <p>Scan to pay:</p>{{qrcode .PaymentURL 120}}
</div>
{{end}}

Example 2: Shipping Label

<div class="shipping-label">
  <div class="label-row">
    <div class="address-block">
      <p class="label-header">FROM:</p>
      <p>{{.ShipFrom.Name}}</p>
      <p>{{.ShipFrom.Street}}</p>
      <p>{{.ShipFrom.City}}, {{.ShipFrom.State}} {{.ShipFrom.Zip}}</p>
    </div>
    <div class="address-block">
      <p class="label-header">TO:</p>
      <p><strong>{{.ShipTo.Name}}</strong></p>
      <p>{{.ShipTo.City}}, {{.ShipTo.State}} {{.ShipTo.Zip}}</p>
    </div>
  </div>
  <div style="text-align: center; margin: 15px 0;">
    {{code128 .TrackingNumber 300 60}}
    <p style="font-family: monospace;">{{.TrackingNumber}}</p>
  </div>
  <div class="label-row">
    <div>{{qrcode .TrackingURL 100}}</div>
    <div class="service-badge">{{upper .ServiceLevel}}</div>
    <div><p>Weight: {{printf "%.1f" .Weight}} lbs</p>
      <p>Ship: {{dateFormat .ShipDate "01/02/2006"}}</p></div>
  </div>
</div>

Example 3: Packing Slip

<h2>Packing Slip</h2>
<p>Order #{{.OrderNumber}} — {{dateFormat .OrderDate "January 2, 2006"}}</p>
<table class="items-table">
  <thead><tr><th>Item</th><th>SKU</th><th>Ordered</th><th>Shipped</th><th>Status</th></tr></thead>
  <tbody>
  {{range .Items}}
  <tr>
    <td>{{.Description}}</td>
    <td style="font-family: monospace;">{{.SKU}}</td>
    <td>{{.QuantityOrdered}}</td>
    <td>{{.QuantityShipped}}</td>
    <td>{{if eq .QuantityOrdered .QuantityShipped}}<span style="color:green">Complete</span>
      {{else if gt .QuantityShipped 0}}<span style="color:orange">Partial</span>
      {{else}}<span style="color:red">Backordered</span>{{end}}</td>
  </tr>
  {{end}}
  </tbody>
</table>
{{if .SpecialInstructions}}
  <h3>Special Instructions</h3>
  <p>{{.SpecialInstructions}}</p>
{{end}}

Template Design Patterns

PatternWhen to UseExample
Guard with ifOptional fields or barcodes{{if .SKU}}{{code128 .SKU 200 50}}{{end}}
Safe access with withNested objects that might be nil{{with .Customer}} ... {{end}}
Default valuesMissing text fields{{if .Notes}}{{.Notes}}{{else}}No notes{{end}}
Alternating rowsTable readability{{if mod $i 2}}class="odd"{{end}}
Conditional stylingStatus indicatorsstyle="color: {{if eq .Status "paid"}}green{{else}}red{{end}}"
Cache barcodeReusing same barcode{{$bc := code128_base64 .SKU 200 50}}
⏸ Pause the Video

Exercise 16: Build a Shipping Label

Build a complete shipping label template from scratch:

  1. Create a new template called "Shipping Label"
  2. Two-column layout: Ship From (left) and Ship To (right)
  3. Code 128 barcode for the tracking number (centered)
  4. QR code for the tracking URL
  5. Service level badge: "OVERNIGHT" in red, "GROUND" in blue, using {{if eq}}
  6. Weight, dimensions, and ship date (formatted with dateFormat)
  7. Style it with a border, monospace tracking number, and bold recipient name
{
  "ShipFrom": {
    "Name": "Acme Corp",
    "Street": "456 Tech Ave",
    "City": "Portland",
    "State": "OR",
    "Zip": "97201"
  },
  "ShipTo": {
    "Name": "Jane Smith",
    "Street": "123 Main St",
    "City": "Seattle",
    "State": "WA",
    "Zip": "98101"
  },
  "TrackingNumber": "1Z999AA10123456784",
  "TrackingURL": "https://track.example.com/1Z999AA10123456784",
  "ServiceLevel": "OVERNIGHT",
  "Weight": 5.2,
  "ShipDate": "2024-03-15"
}

Exercise 16: Solution

<div style="border:3px solid #000; padding:20px; font-family:Arial; max-width:500px;">
  <div style="display:flex; justify-content:space-between; margin-bottom:15px;">
    <div>
      <p style="font-size:8pt; color:#666; text-transform:uppercase;">From:</p>
      <p>{{.ShipFrom.Name}}</p>
      <p>{{.ShipFrom.Street}}</p>
      <p>{{.ShipFrom.City}}, {{.ShipFrom.State}} {{.ShipFrom.Zip}}</p>
    </div>
    <div>
      <p style="font-size:8pt; color:#666; text-transform:uppercase;">To:</p>
      <p><strong style="font-size:14pt;">{{.ShipTo.Name}}</strong></p>
      <p>{{.ShipTo.Street}}</p>
      <p>{{.ShipTo.City}}, {{.ShipTo.State}} {{.ShipTo.Zip}}</p>
    </div>
  </div>
  <div style="text-align:center; border-top:2px solid #000; border-bottom:2px solid #000; padding:10px 0;">
    {{code128 .TrackingNumber 300 60}}
    <p style="font-family:monospace;">{{.TrackingNumber}}</p>
  </div>
  <div style="display:flex; justify-content:space-between; align-items:center; margin-top:10px;">
    <div>{{qrcode .TrackingURL 90}}</div>
    <div style="padding:8px 20px; font-weight:bold; font-size:14pt; border-radius:4px;
      {{if eq .ServiceLevel "OVERNIGHT"}}background:#fed7d7; color:#c53030;
      {{else}}background:#bee3f8; color:#2b6cb0;{{end}}">{{upper .ServiceLevel}}</div>
    <div style="text-align:right; font-size:9pt;">
      <p>Weight: {{printf "%.1f" .Weight}} lbs</p>
      <p>Ship: {{dateFormat .ShipDate "Jan 2, 2006"}}</p></div>
  </div>
</div>

Section 12

Troubleshooting & Wrap-Up

Fix common errors and review what you've learned

Common Syntax Errors

Missing {{end}}

<!-- WRONG -->
{{if .IsVIP}}
    <span>VIP</span>

<!-- RIGHT -->
{{if .IsVIP}}
    <span>VIP</span>
{{end}}

Missing Braces

<!-- WRONG -->
{{if .IsActive}
    <p>Active</p>
{end}}

<!-- RIGHT -->
{{if .IsActive}}
    <p>Active</p>
{{end}}
#1 error: Every {{if}}, {{range}}, and {{with}} needs a matching {{end}}. Count your opening and closing tags!

Data Access Errors

Wrong Field Name

<!-- Error: "can't evaluate field FullName in type" -->
{{.Customer.FullName}}     <!-- Field doesn't exist -->
{{.Customer.Name}}         <!-- Correct -->

Nil Pointer

<!-- Error: "nil pointer evaluation" -->
{{.Customer.Address.Street}}  <!-- Crashes if Address is nil -->

<!-- Safe: use 'with' -->
{{with .Customer}}
    {{with .Address}}
        {{.Street}}
    {{end}}
{{end}}

Wrong Function Name

<!-- Error: "function 'formatdate' not defined" -->
{{formatdate .Date "01/02/2006"}}     <!-- Wrong -->
{{dateFormat .Date "01/02/2006"}}     <!-- Correct -->

Debugging Templates

Dump the entire data context

<!-- Show all available data -->
<pre>{{printf "%+v" .}}</pre>

<!-- Show a specific field -->
<pre>Customer: {{printf "%+v" .Customer}}</pre>

<!-- Check if a field exists -->
{{if isSet .Discount}}
    Discount exists: {{.Discount}}
{{else}}
    Discount field is nil
{{end}}

<!-- Check if non-empty -->
{{if notEmpty .Notes}}
    Notes: {{.Notes}}
{{else}}
    Notes field is empty or nil
{{end}}
Debug workflow: Add <pre>{{printf "%+v" .}}</pre> at the top of your template to see all available data. Remove it when done debugging.

Troubleshooting Checklist

ProblemCheck
Template won't parseCount {{if}}/{{range}}/{{with}} vs {{end}} tags
Field shows blankVerify JSON key name matches exactly (case-sensitive)
Nil pointer errorWrap nested access in {{with}} or {{if}}
Function not foundCheck spelling and case: dateFormat not formatDate
Barcode not showingGuard with {{if .Field}}, check data isn't empty
Chart not renderingVerify data format (labels + datasets) and unique elementID
Page breaks not workingUse page-break-before: always on a block element
Backgrounds missing in PDFEnable "Print backgrounds" in Settings
Wrong date formatUse Go reference date: 01=month, 02=day, 2006=year
Styles not applyingCheck CSS tab for typos; verify class names match

What We Covered

SectionKey Skills
Go Template SyntaxFields, conditionals, loops, functions, variables
Template EditorHTML/CSS/JSON tabs, preview, drafts, docs
CSS StylingLayout, page breaks, print styles, typography
Document SettingsPage size, margins, headers/footers, TTL
Barcodes & QR CodesCode 128, EAN-13, QR, styling, base64 variants
ChartsLine, bar, pie, doughnut, CSV & object formats
Version ControlBranches, commits, diffs, merges, rollback
ApprovalsRequest, review, approve/reject workflow
Script IntegrationgenerateSync, generate, email delivery, scheduling
Real-World ExamplesInvoice, shipping label, report, packing slip
TroubleshootingDebug tools, common errors, defensive patterns

Next Steps

  • Build a real template — start with a document your team actually needs
  • Explore the Docs tab — there's more to discover beyond what we covered
  • Try Label Mode — if you need product or shipping labels
  • Connect to the Script Engine — automate document generation
  • Set up approvals — for compliance-sensitive templates
  • Use CSS Presets — for quick professional styling
Remember: The Docs tab in the template editor is always available as a quick reference. You don't need to memorize everything!

Thank You!

You're now ready to create professional documents with DataMagik

Happy templating!