DataMagik Documents
Document Designer Guide
Create, manage, and version professional PDF document templates using HTML, CSS, and Go template syntax.
Table of Contents
- Overview
- Creating a Template
- The Template Editor
- Go Template Syntax
- Working with Data
- Barcodes & QR Codes
- ZPL Label Creation & Test Printing
- User Defined Tables in Templates
- Serial Number Series
- Charts & Visualizations
- Version Control & Branches
- Approval Workflow
- Generating Documents
- Best Practices
1. Overview
The Document Designer allows you to create professional PDF documents using familiar web technologies (HTML & CSS) combined with Go's powerful templating system. Documents can include:
- Dynamic data from your ERP, scripts, or APIs
- Barcodes and QR codes
- Charts and visualizations
- Tables with repeating data
- Conditional content
- Custom styling and branding
Key Features:
- Visual Preview — See your template rendered with sample data
- Version Control — Track changes with branch-based versioning
- Approval Workflow — Require approval before templates go to production
- Audit Trail — Full history of who changed what and when
2. Creating a Template
Step 1: Open Document Designer
Navigate to DataMagik → Document Designer in the main menu.
Step 2: Click "New Template"
Click the purple New Template button in the toolbar.
Step 3: Configure Template Settings
FieldDescriptionExampleNameTemplate identifierInvoice - StandardCategoryGrouping for organizationInvoicesDescriptionWhat this template is forStandard customer invoice with line items
Step 4: Start Editing
After creation, click Edit to open the template editor.
3. The Template Editor
The template editor provides a full-featured HTML/CSS editing environment.
Editor Layout
- Left Panel — Code editor with syntax highlighting
- Right Panel — Live preview with sample data
- Top Toolbar — Save, preview, settings, and help
Editor Tabs
- HTML — Template structure and Go template tags
- CSS — Styling for your document
- Sample Data — JSON data for preview
Keyboard Shortcuts
ShortcutActionCtrl+SSave templateCtrl+Shift+PPreview with current dataCtrl+SpaceShow autocompleteCtrl+/Toggle commentCtrl+?Open help modal
4. Go Template Syntax
DataMagik templates use Go's template syntax. Here's a comprehensive reference:
Basic Output
<!-- Output a field value -->
<p>Customer: {{.CustomerName}}</p>
<!-- Access nested fields -->
<p>City: {{.Address.City}}</p>
<!-- Access array elements -->
<p>First Item: {{index .Items 0}}</p>
Variables
<!-- Define a variable -->
{{$total := .OrderTotal}}
<!-- Use the variable -->
<p>Total: ${{$total}}</p>
<!-- Assign from calculation -->
{{$discounted := mul .Price 0.9}}
<p>Discounted: ${{$discounted}}</p>
Conditionals
<!-- Simple if -->
{{if .IsRush}}
<div class="rush-banner">RUSH ORDER</div>
{{end}}
<!-- If-else -->
{{if .IsPaid}}
<span class="status paid">PAID</span>
{{else}}
<span class="status unpaid">UNPAID</span>
{{end}}
<!-- If-else if-else -->
{{if eq .Status "shipped"}}
<span class="shipped">Shipped</span>
{{else if eq .Status "processing"}}
<span class="processing">Processing</span>
{{else}}
<span class="pending">Pending</span>
{{end}}
Comparison Operators
OperatorDescriptionExampleeqEqual{{if eq .Status "active"}}neNot equal{{if ne .Count 0}}ltLess than{{if lt .Quantity 10}}leLess than or equal{{if le .Age 18}}gtGreater than{{if gt .Total 1000}}geGreater than or equal{{if ge .Score 70}}
Logical Operators
<!-- AND -->
{{if and .IsActive .IsVerified}}
<p>User is active and verified</p>
{{end}}
<!-- OR -->
{{if or .IsAdmin .IsModerator}}
<p>User has elevated permissions</p>
{{end}}
<!-- NOT -->
{{if not .IsDeleted}}
<p>Record is active</p>
{{end}}
<!-- Combined -->
{{if and .IsActive (or .IsAdmin .IsModerator)}}
<p>Active user with permissions</p>
{{end}}
Loops
<!-- Simple loop -->
<ul>
{{range .Items}}
<li>{{.Name}} - ${{.Price}}</li>
{{end}}
</ul>
<!-- Loop with index -->
<table>
{{range $index, $item := .Items}}
<tr>
<td>{{add $index 1}}</td>
<td>{{$item.Name}}</td>
<td>{{$item.Quantity}}</td>
</tr>
{{end}}
</table>
<!-- Loop with else (empty case) -->
{{range .Items}}
<div>{{.Name}}</div>
{{else}}
<div>No items found</div>
{{end}}
Built-in Functions
FunctionDescriptionExamplelenLength of array/string{{len .Items}}indexGet item at index{{index .Items 0}}printfFormatted string{{printf "%.2f" .Price}}upperUppercase{{upper .Code}}lowerLowercase{{lower .Email}}titleTitle case{{title .Name}}trimTrim whitespace{{trim .Input}}
Math Functions
<!-- Addition -->
<p>Subtotal: ${{add .Price .Tax}}</p>
<!-- Subtraction -->
<p>Discount: ${{sub .Total .Discount}}</p>
<!-- Multiplication -->
<p>Extended: ${{mul .Quantity .UnitPrice}}</p>
<!-- Division -->
<p>Average: {{div .Total .Count}}</p>
<!-- Modulo -->
{{if eq (mod $index 2) 0}}
<tr class="even">
{{else}}
</tr>
<tr class="odd">
{{end}}
</tr>
Date Formatting
<!-- Format date -->
<p>Date: {{formatDate .OrderDate "January 2, 2006"}}</p>
<p>Date: {{formatDate .OrderDate "2006-01-02"}}</p>
<p>Date: {{formatDate .OrderDate "01/02/2006"}}</p>
<!-- Current date -->
<p>Printed: {{now | formatDate "2006-01-02 15:04:05"}}</p>
Comments
{{/* This is a comment - won't appear in output */}} {{/* Multi-line comment for
documentation */}}
5. Working with Data
Sample Data Structure
Your sample data should mirror the structure of real data that will be passed during generation:
{
"OrderNumber": "ORD-2025-001234",
"OrderDate": "2025-12-05",
"CustomerName": "Acme Corporation",
"CustomerCode": "ACME001",
"BillTo": {
"Name": "Acme Corporation",
"Address": "123 Main Street",
"City": "Detroit",
"State": "MI",
"Zip": "48201"
},
"ShipTo": {
"Name": "Acme Warehouse",
"Address": "456 Industrial Blvd",
"City": "Warren",
"State": "MI",
"Zip": "48089"
},
"Items": [
{
"LineNumber": 1,
"PartNumber": "WIDGET-001",
"Description": "Standard Widget",
"Quantity": 100,
"UnitPrice": 12.5,
"ExtendedPrice": 1250.0
},
{
"LineNumber": 2,
"PartNumber": "GADGET-002",
"Description": "Premium Gadget",
"Quantity": 50,
"UnitPrice": 25.0,
"ExtendedPrice": 1250.0
}
],
"Subtotal": 2500.0,
"TaxRate": 0.06,
"TaxAmount": 150.0,
"Total": 2650.0,
"IsPaid": false,
"IsRush": true
}
Building a Document
Header Section:
<div class="header">
<div class="logo">
<img src="{{.CompanyLogo}}" alt="Logo" />
</div>
<div class="document-info">
<h1>INVOICE</h1>
<p>Invoice #: {{.OrderNumber}}</p>
<p>Date: {{formatDate .OrderDate "January 2, 2006"}}</p>
</div>
</div>
Address Block:
<div class="addresses">
<div class="bill-to">
<h3>Bill To:</h3>
<p>{{.BillTo.Name}}</p>
<p>{{.BillTo.Address}}</p>
<p>{{.BillTo.City}}, {{.BillTo.State}} {{.BillTo.Zip}}</p>
</div>
<div class="ship-to">
<h3>Ship To:</h3>
<p>{{.ShipTo.Name}}</p>
<p>{{.ShipTo.Address}}</p>
<p>{{.ShipTo.City}}, {{.ShipTo.State}} {{.ShipTo.Zip}}</p>
</div>
</div>
Line Items Table:
<table class="line-items">
<thead>
<tr>
<th>Line</th>
<th>Part #</th>
<th>Description</th>
<th>Qty</th>
<th>Unit Price</th>
<th>Extended</th>
</tr>
</thead>
<tbody>
{{range .Items}}
<tr>
<td>{{.LineNumber}}</td>
<td>{{.PartNumber}}</td>
<td>{{.Description}}</td>
<td>{{.Quantity}}</td>
<td>${{printf "%.2f" .UnitPrice}}</td>
<td>${{printf "%.2f" .ExtendedPrice}}</td>
</tr>
{{end}}
</tbody>
<tfoot>
<tr>
<td colspan="5" class="text-right">Subtotal:</td>
<td>${{printf "%.2f" .Subtotal}}</td>
</tr>
<tr>
<td colspan="5" class="text-right">Tax ({{mul .TaxRate 100}}%):</td>
<td>${{printf "%.2f" .TaxAmount}}</td>
</tr>
<tr class="total-row">
<td colspan="5" class="text-right"><strong>Total:</strong></td>
<td><strong>${{printf "%.2f" .Total}}</strong></td>
</tr>
</tfoot>
</table>
6. Barcodes & QR Codes
DataMagik supports generating barcodes directly in your templates.
Supported Barcode Types
1D Barcodes:
- Code 128 (general purpose)
- Code 39 (alphanumeric)
- EAN-13, EAN-8 (retail)
- UPC-A, UPC-E (US retail)
- Codabar (libraries, blood banks)
2D Barcodes:
- QR Code (high capacity, URLs)
- Data Matrix (compact, industrial)
- PDF417 (documents, IDs)
- Aztec (tickets, boarding passes)
Barcode Functions
<!-- Code 128 (most common) -->
<img src="data:image/png;base64,{{barcode128 .OrderNumber}}" alt="Barcode" />
<!-- Code 128 with custom size -->
<img
src="data:image/png;base64,{{barcode128 .OrderNumber 300 60}}"
alt="Barcode"
/>
<!-- QR Code -->
<img src="data:image/png;base64,{{qrcode .TrackingURL}}" alt="QR Code" />
<!-- QR Code with size -->
<img src="data:image/png;base64,{{qrcode .TrackingURL 150}}" alt="QR Code" />
<!-- EAN-13 -->
<img src="data:image/png;base64,{{ean13 .ProductEAN}}" alt="EAN" />
<!-- Data Matrix -->
<img
src="data:image/png;base64,{{datamatrix .SerialNumber}}"
alt="Data Matrix"
/>
Barcode Best Practices
- Test scanning — Always test generated barcodes with actual scanners
- Adequate size — Ensure barcodes are large enough for reliable scanning
- Quiet zones — Leave white space around barcodes
- Proper contrast — Use dark barcodes on light backgrounds
7. ZPL Label Creation & Test Printing
DataMagik supports creating ZPL (Zebra Programming Language) labels for thermal printers, with integrated test printing capabilities.
What is ZPL?
ZPL is a command language used by Zebra and compatible thermal label printers. It allows precise control over label layout, barcodes, text, and graphics.
ZPL Template Syntax
ZPL templates use the same Go template syntax as PDF documents:
^XA
^FO50,50^A0N,40,40^FD{{.PartNumber}}^FS
^FO50,100^A0N,30,30^FD{{.Description}}^FS
^FO50,150^BY3^BCN,100,Y,N,N^FD{{.Barcode}}^FS
^FO50,280^A0N,25,25^FDQty: {{.Quantity}}^FS
^FO50,320^A0N,20,20^FD{{formatDate .PrintDate "2006-01-02"}}^FS
^XZ
Common ZPL Commands
CommandDescriptionExample^XAStart label formatAlways first^XZEnd label formatAlways last^FOField origin (x,y position)^FO50,100^FDField data (text content)^FD{{.Text}}^FS^FSField separator (end field)Required after data^A0Font selection^A0N,40,40 (normal, h, w)^BCCode 128 barcode^BCN,100,Y,N,N^BQQR Code^BQN,2,5^BYBarcode defaults^BY3 (module width)^GBGraphic box (lines/borders)^GB400,3,3^FS
Creating a ZPL Template
- Create a new template in Document Designer
- Set the template type to ZPL Label
- Enter your ZPL code with Go template syntax
- Configure sample data for preview
Test Printing
Test print labels directly from the Document Designer:
- Open your ZPL template
- Click Test Print in the toolbar
- Select a printer from the dropdown
- Review the preview
- Click Print
Printer Configuration
Printers are configured in Manufacturing → Printers:
SettingDescriptionNameFriendly printer nameIP AddressPrinter network addressPortUsually 9100 for ZPLProtocolRaw Socket (ZPL/Direct)DPI203 or 300 typicallyZPL CompatibleEnable for ZPL printers
Printing from Scripts
Use the manufacturing and zpl APIs:
function main(context) {
// Render ZPL template with data
const zplCode = zpl.fromTemplate("PartLabel", {
partNumber: context.partNumber,
description: context.description,
barcode: context.partNumber,
quantity: context.qty,
});
// Print to specific printer
manufacturing.printLabel("Warehouse-Zebra-01", zplCode);
return { success: true, script_message: "Label printed" };
}
Batch Printing
Print multiple labels efficiently:
function main(context) {
const items = context.items; // Array of items to label
const batchResult = manufacturing.printBatchLabels(
"Line1-Zebra",
"PartLabel",
items.map((item) => ({
partNumber: item.pn,
description: item.desc,
barcode: item.pn,
quantity: item.qty,
}))
);
return {
success: true,
script_message: `Printed ${batchResult.count} labels`,
};
}
ZPL Best Practices
- Test on actual printers — Emulators may render differently
- Use consistent coordinates — Align elements on a grid
- Account for label size — Know your label dimensions in dots
- Include quiet zones — Leave margins around barcodes
- Test barcode scanning — Verify readability before production
8. User Defined Tables in Templates
Access data from User Defined Tables directly in your document templates.
Querying Tables in Sample Data
Your sample data can include table lookups:
{
"OrderNumber": "ORD-001",
"ShippingMethods": "{{tableQuery 'shipping_methods' 'active' true}}",
"CustomerData": "{{tableGet 'customers' 'CUST001'}}"
}
Using Table Data in Scripts
When generating documents from scripts, query tables and pass data:
function main(context) {
// Get data from User Defined Tables
const customer = tables.get("customers", context.customerId);
const products = tables.query("products", { category: context.category });
const shippingOptions = tables.list("shipping_methods");
// Generate document with table data
documents.generate("Order Confirmation", {
orderNumber: context.orderNumber,
customer: customer,
products: products,
shippingOptions: shippingOptions,
});
return { success: true };
}
Common Table Patterns
Lookup Table for Dropdowns:
// Get list of valid statuses for display
const statuses = tables.list("order_statuses");
const statusName = statuses.find((s) => s.code === order.status)?.name;
Customer Master Data:
// Get customer details for invoice
const customer = tables.get("customers", invoiceData.customerCode);
const billingAddress = tables.get("addresses", customer.billingAddressId);
Product Catalog:
// Get product details with pricing
const product = tables.get("products", lineItem.productId);
const pricing = tables.query("pricing", {
productId: product.id,
priceLevel: customer.priceLevel,
});
Table Functions Available in Templates
FunctionDescriptiontables.get(tableName, key)Get single record by primary keytables.query(tableName, filter)Query records matching filtertables.list(tableName)Get all records from tabletables.exists(tableName, key)Check if record exists
Example: Invoice with Customer Lookup
function main(context) {
// Get customer from UDT
const customer = tables.get("customers", context.customerId);
// Get line items with product details
const lineItems = context.orderLines.map((line) => {
const product = tables.get("products", line.productId);
return {
...line,
productName: product.name,
productDescription: product.description,
unitOfMeasure: product.uom,
};
});
// Get company info from settings table
const companyInfo = tables.get("settings", "company_info");
documents.generate("Invoice - Standard", {
invoiceNumber: context.invoiceNumber,
customer: customer,
lineItems: lineItems,
company: companyInfo,
total: context.total,
});
return { success: true };
}
9. Serial Number Series
Integrate with Traceability serial number series for automatic serial generation in documents.
Generating Serials in Templates
Use the manufacturing API to generate serial numbers:
function main(context) {
// Generate serial for this document
const serialNumber = manufacturing.generateSerial("ProductSerials", {
workOrder: context.workOrderNumber,
partNumber: context.partNumber,
});
documents.generate("Product Label", {
serialNumber: serialNumber,
partNumber: context.partNumber,
date: new Date().toISOString(),
});
return { success: true, data: { serial: serialNumber } };
}
Batch Serial Generation
Generate multiple serials for batch labels:
function main(context) {
const quantity = context.quantity;
// Generate batch of serials
const serials = manufacturing.generateSerials("ProductSerials", quantity, {
workOrder: context.workOrderNumber,
partNumber: context.partNumber,
});
// Generate a label for each serial
for (const serial of serials) {
documents.generate("Product Label", {
serialNumber: serial,
partNumber: context.partNumber,
workOrder: context.workOrderNumber,
});
}
return {
success: true,
script_message: `Generated ${serials.length} serial numbers`,
data: { serials: serials },
};
}
Serial Number Formats
Serial numbers are formatted according to the series configuration:
TokenDescriptionExample{YYYY}4-digit year2025{YY}2-digit year25{MM}Month06{DD}Day15{JJJ}Julian day166{####}Sequence0042{SEQUENCE}Sequence (alias)0042
Example format: SN-{YYYY}{JJJ}-{####} → SN-2025166-0042
Using Serials in ZPL Labels
function main(context) {
// Generate serial
const serial = manufacturing.generateSerial("PartSerials");
// Create ZPL label with serial
const zplCode = zpl.fromTemplate("SerialLabel", {
serialNumber: serial,
partNumber: context.partNumber,
barcode: serial,
});
// Print the label
manufacturing.printLabel(context.printer, zplCode);
return { success: true, data: { serial: serial } };
}
Serial + UDT Integration
Combine serial generation with table lookups:
function main(context) {
// Get product info from UDT
const product = tables.get("products", context.productId);
// Get the correct serial series for this product
const seriesName = product.serialSeries || "DefaultSerials";
// Generate serial
const serial = manufacturing.generateSerial(seriesName, {
partNumber: product.partNumber,
workOrder: context.workOrder,
});
// Generate label with all data
documents.generate("Product Certificate", {
serialNumber: serial,
product: product,
manufactureDate: new Date().toISOString(),
workOrder: context.workOrder,
});
return { success: true };
}
10. Charts & Visualizations
Create dynamic charts using the built-in charting functions.
Supported Chart Types
- Line Chart
- Bar Chart
- Pie Chart
- Doughnut Chart
Chart Syntax
<!-- Bar chart from data -->
{{chart "bar" .SalesData "Month" "Revenue"}}
<!-- Pie chart -->
{{chart "pie" .CategoryBreakdown "Category" "Amount"}}
<!-- Line chart with options -->
{{chart "line" .TrendData "Date" "Value" "title=Sales Trend"}}
Chart Data Format
Your data should be an array of objects:
{
"SalesData": [
{ "Month": "Jan", "Revenue": 45000 },
{ "Month": "Feb", "Revenue": 52000 },
{ "Month": "Mar", "Revenue": 48000 }
]
}
📸 Screenshot Needed: A document preview showing a chart rendered in the template.
11. Version Control & Branches
Document Designer includes Git-like version control for templates.
Branches
- main — Production-ready templates
- draft — Work-in-progress changes
- Custom branches — Feature development or customer-specific versions
Creating a Version
- Make changes in the editor
- Click Commit in the toolbar
- Enter a commit message describing your changes
- Select the branch to commit to
- Click Commit Changes
Viewing Version History
- Select a template
- Click the Versions tab in the preview panel
- View list of all versions with timestamps and authors
- Click a version to view its content
Comparing Versions
- Select two versions in the history
- Click Compare
- View side-by-side diff of changes
Rolling Back
- Find the version you want to restore
- Click Rollback to this version
- Confirm the rollback
- A new version is created with the restored content
12. Approval Workflow
For production environments, templates can require approval before use.
Requesting Approval
- Commit your changes to a version
- Click Request Approval
- Select the version to approve
- Add any notes for the approver
- Submit the request
Approving a Template
- Go to the Approvals tab
- Review pending approval requests
- Click on a request to see details
- Review the template content and changes
- Click Approve or Reject
- Add approval notes if needed
📸 Screenshot Needed: The Approvals tab showing pending approval requests.
Approval Status
StatusDescriptionPendingWaiting for approvalApprovedTemplate can be used in productionRejectedChanges needed before approval
13. Generating Documents
From Scripts
function main(context) {
documents.generate("Invoice - Standard", {
OrderNumber: context.orderNumber,
CustomerName: context.customerName,
Items: context.lineItems,
Total: context.total,
});
return { success: true };
}
From Automations
Documents can be triggered from:
- Webhook automations
- Scheduled tasks
- Browser extension actions
- n8n workflows
Generation Options
// Async generation (fire and forget)
const requestId = documents.generate("Template Name", data);
// Sync generation (wait for result)
const result = documents.generateSync("Template Name", data, {
returnType: "url", // "url" or "binary"
timeout: 60000, // milliseconds
});
// Check status
const status = documents.getStatus(requestId);
Viewing Generated Documents
- Select a template
- Click the Documents tab
- View list of generated documents
- Click to download or preview
14. Best Practices
Template Organization
- Use categories — Group related templates (Invoices, Packing Slips, Labels)
- Descriptive names —
Invoice - Standardnotinv1 - Document in description — Explain when/how template is used
Code Quality
- Keep it simple — Break complex templates into sections
- Use variables — Store calculated values in variables for reuse
- Comment your code — Use
{{/* comments */}}to explain complex logic - Consistent styling — Use CSS classes, not inline styles
Testing
- Test with edge cases — Empty arrays, null values, long text
- Test printing — PDF rendering may differ from screen preview
- Test barcodes — Verify they scan correctly
Performance
- Optimize images — Use appropriate sizes, compress when possible
- Limit complexity — Very complex templates may be slow to render
- Test with real data volume — Large tables may need pagination
CSS Tips for PDF
/* Force page breaks */
.page-break {
page-break-before: always;
}
/* Prevent element from breaking across pages */
.keep-together {
page-break-inside: avoid;
}
/* Set page size and margins */
@page {
size: letter;
margin: 0.5in;
}
/* Header on every page */
@page {
@top-center {
content: "Company Name";
}
}
/* Page numbers */
@page {
@bottom-right {
content: "Page " counter(page) " of " counter(pages);
}
}
Screenshots Summary
Please capture the following screenshots to complete this documentation:
- Document Designer main interface - Template list and preview panel
- New Template button - In the toolbar
- Template Editor - Code editor and live preview
- Barcode example - Document with barcode/QR code rendered
- ZPL template editor - ZPL code and label preview
- Test Print dialog - Printer selection for test printing
- Serial number in document - Generated serial number preview
- Chart example - Document with chart rendered
- Branch selector - Dropdown showing branches
- Versions tab - Version history with commits
- Request Approval - Button and modal
- Approvals tab - Pending requests
- Documents tab - List of generated documents