Order, Quote, And Proforma PDF Templates
Order, quote, and proforma PDFs each have their own document template. This lets you keep different wording, layout, and fields for each document type.
Template Selection
PDF generation uses the template selected for the matching document type:
| Document | Setting | Template type |
|---|---|---|
| Order | documents.orderPdfDocumentId | order |
| Quote | documents.quotePdfDocumentId | quote |
| Proforma | documents.proformaPdfDocumentId | proforma |
If no specific template is selected, the system uses the default template for that document type. Older legacy offer/proforma templates can still be used for compatibility.
Template Schema
New templates should use:
renderSchema = orderDocument
renderSchemaVersion = 1This schema provides a stable field set for order, quote, and proforma PDFs. Older templates can stay on the legacy schema until they are intentionally updated.
Changing visual HTML does not require a schema version change. A new schema version is only needed when existing field names or field meanings change.
Common Fields
The main document fields are available at the template root:
| Field | Meaning |
|---|---|
.documentType | order, quote, or proforma |
.orderId | Order/proforma ID |
.quoteId | Quote ID |
.date | Document date |
.companyName | Buyer company name |
.companyInfo | Buyer company details |
.shippingAddress | Shipping address |
.billingAddress | Billing address |
.lineItems | Product/service lines |
.linesByHeadings | PDF render-only product/service lines grouped under document headings |
.currency | Document currency |
.totalExclTax | Total excluding tax |
.totalTax | Tax total |
.totalInclTax | Total including tax |
.comments | Internal/public document comments |
.customerNote | Customer note |
.deliveryInstructions | Delivery instructions |
Dates are JSON date-time values and numbers are JSON numbers. Empty optional fields may be missing or rendered as an empty value.
Field Types And Nested Objects
Root fields:
| Field | Type | Included |
|---|---|---|
.schemaVersion | string | orderDocument.v1 |
.documentType | string | order, quote, or proforma |
.orderId | number | Present for orders and proformas |
.quoteId | number | Present for quotes |
.date | date-time | Document date |
.createdAt | date-time | Creation date/time |
.companyId | number | Buyer company ID |
.companyName | string | Buyer company name |
.title | string | Document title/body intro |
.document | string | Existing document number field, if set |
.comments | string | Document comments |
.customerNote | string | Customer-facing note |
.deliveryInstructions | string | Delivery instructions |
.email | string | Buyer/order email |
.externalUserId | number | Linked external user ID |
.paymentMethod | string | Payment method |
.paymentProvider | string | Payment provider |
.paymentIsCod | boolean | Cash-on-delivery flag |
.paymentReference | string | Payment reference |
.paymentTermId | number | Payment term ID |
.shippingMethod | string | Shipping method display name |
.shippingMethodCode | string | Shipping method code |
.trackingNumber | string | Shipment tracking number |
.trackingCarrier | string | Tracking carrier/code |
.currency | string | Currency code, for example EUR |
.totalExclTax | number | Total excluding tax |
.totalTax | number | Tax total |
.totalInclTax | number | Total including tax |
.totalDiscountInclTax | number | Discount including tax |
.totalShippingInclTax | number | Shipping including tax |
.totalDiscountExclTax | number | Discount excluding tax |
.totalShippingExclTax | number | Shipping excluding tax |
.status | object | Current status |
.companyInfo | object | Buyer company details |
.shippingAddress | object | Shipping address |
.billingAddress | object | Billing address |
.pickupLocation | object | Pickup point/location, when used |
.companyAddressInfo | object | Buyer contact/address details, when available |
.customFields | object | Tenant-specific custom fields, when configured |
.lineItems | array | Flat line list |
.linesByHeadings | array | PDF render-only grouped line list |
.documentCharges | array | Document-level charges |
.invoiceInfo | object | Linked invoice information, when formed |
.companyInfo:
| Field | Type | Included |
|---|---|---|
.companyInfo.companyId | number | Buyer company ID |
.companyInfo.companyName | string | Buyer company name |
.companyInfo.companyCode | string | Buyer registration/company code |
.companyInfo.companyVatCode | string | Buyer VAT code |
.companyInfo.isPerson | boolean | True when buyer is a person |
.companyInfo.address | string | Buyer address |
.companyInfo.postCode | string | Buyer post code |
.companyInfo.city | string | Buyer city |
.companyInfo.country | string | Buyer country |
.companyInfo.phoneNumber | string | Buyer phone number |
.shippingAddress:
| Field | Type | Included |
|---|---|---|
.shippingAddress.addressId | number | Address ID |
.shippingAddress.name | string | Recipient/company display name |
.shippingAddress.firstName | string | Recipient first name |
.shippingAddress.lastName | string | Recipient last name |
.shippingAddress.companyName | string | Deprecated alias, use .name |
.shippingAddress.address | string | Street/address line |
.shippingAddress.city | string | City |
.shippingAddress.country | string | Country name |
.shippingAddress.countryName | string | Country name resolved from country code when available |
.shippingAddress.countryCode | string | Country code |
.shippingAddress.postCode | string | Post code |
.shippingAddress.homePhone | string | Home phone |
.shippingAddress.mobilePhone | string | Mobile phone |
.billingAddress:
| Field | Type | Included |
|---|---|---|
.billingAddress.addressId | number | Address ID |
.billingAddress.firstName | string | First name |
.billingAddress.lastName | string | Last name |
.billingAddress.companyName | string | Company name |
.billingAddress.address | string | Street/address line |
.billingAddress.city | string | City |
.billingAddress.countryName | string | Country name resolved from country code when available |
.billingAddress.countryCode | string | Country code |
.billingAddress.postCode | string | Post code |
.billingAddress.phone | string | Phone |
.pickupLocation:
| Field | Type | Included |
|---|---|---|
.pickupLocation.locationId | number | Pickup location ID |
.pickupLocation.locationName | string | Pickup location name |
.pickupLocation.locationCity | string | Pickup city |
.pickupLocation.locationPostCode | string | Pickup post code |
.pickupLocation.locationCompanyCode | string | Pickup company code |
.companyAddressInfo:
| Field | Type | Included |
|---|---|---|
.companyAddressInfo.contactId | number | Contact/address ID |
.companyAddressInfo.title | string | Contact title/display name |
.companyAddressInfo.firstName | string | First name |
.companyAddressInfo.lastName | string | Last name |
.companyAddressInfo.mobilePhone | string | Mobile phone |
.companyAddressInfo.email | string | |
.companyAddressInfo.contactType | string | Contact type |
.companyAddressInfo.address | string | Address line |
.companyAddressInfo.addressLine2 | string | Second address line |
.companyAddressInfo.region | string | Region |
.companyAddressInfo.city | string | City |
.companyAddressInfo.country | string | Country |
.companyAddressInfo.postCode | string | Post code |
.status:
| Field | Type | Included |
|---|---|---|
.status.statusId | number | Status ID |
.status.code | string | Status code |
.status.name | string | Status name |
.status.formInvoiceOnChange | boolean | Whether status forms invoice on change |
.status.isInvoiceDownloadable | boolean | Whether invoice download is allowed |
.status.showAsPaid | boolean | Whether status is shown as paid |
.status.isWaitingForPayment | boolean | Whether status waits for payment |
.status.isCancelled | boolean | Whether status is cancelled |
.status.isOrderCompleted | boolean | Whether order is completed |
.status.isActive | boolean | Whether status is active |
.status.includeInOrderedQuantity | boolean | Whether lines count in ordered quantity |
Line items are available through:
{{ range .lineItems }}
{{ .title }} {{ .quantity }} {{ .measurementUnit }}
{{ formatPrice .priceExclTax }} {{ .currency }}
{{ formatPrice .totalExclTax }} {{ .currency }}
{{ end }}.lineItems[]:
| Field | Type | Included |
|---|---|---|
.id | number | Internal line ID |
.lineType | number | 0 product/service, 1 heading, 2 note |
.title | string | Line title |
.description | string | Line description |
.productId | number | Product ID |
.productName | string | Product name |
.articule | string | Product article/code |
.quantity | number | Quantity |
.measurementUnit | string | Measurement unit |
.currency | string | Currency code |
.priceExclTax | number | Unit price excluding tax |
.priceInclTax | number | Unit price including tax |
.originalPrice | number | Original unit price, if stored |
.taxPercentage | number | Tax percentage |
.taxCode | string | Tax classification code |
.taxAmount | number | Fixed tax amount, when used |
.discountPerUnit | number | Discount per unit |
.discountPercent | number | Discount percentage |
.priceIncludesDiscount | boolean | Whether unit price already includes line discount |
.totalExclTax | number | Line total excluding tax |
.totalInclTax | number | Line total including tax |
.totalDiscountExclTax | number | Line discount total excluding tax |
.charges | array | Line-level charges |
For document layouts that need section headings, use .linesByHeadings. This field is template-only and is not exposed on the external order-detail API:
{{ range .linesByHeadings }}
{{ if .title }}<strong>{{ .title }}</strong>{{ end }}
{{ range .lines }}
{{ if eq .lineTypeString "2" }}
{{ nl2br .title }}
{{ else }}
{{ .title }} {{ .quantity }} {{ .measurementUnit }}
{{ formatPrice .totalExclTax }} {{ .currency }}
{{ end }}
{{ end }}
{{ end }}.linesByHeadings[]:
| Field | Type | Included |
|---|---|---|
.title | string | Heading title |
.totalExclTax | number | Group total excluding tax |
.totalTax | number | Group tax total |
.totalInclTax | number | Group total including tax |
.currency | string | Group currency |
.lines | array | Lines under the heading |
.linesByHeadings[].lines[] uses the same fields as .lineItems[], plus .lineTypeString for legacy-compatible note checks in templates.
.documentCharges[] and .lineItems[].charges[]:
| Field | Type | Included |
|---|---|---|
.chargeId | number | Charge row ID |
.chargeTypeId | number | Charge type ID |
.chargeCode | string | Charge code |
.chargeName | string | Charge name |
.calculationType | string | percentage, fixedAmount, or perUnit |
.percentage | number | Percentage, when used |
.baseAmount | number | Amount the charge was calculated from |
.chargeAmount | number | Charge amount excluding tax |
.isTaxable | boolean | Whether charge is taxable |
.taxPercentage | number | Tax percentage |
.taxAmount | number | Tax amount |
.totalAmount | number | Charge amount including tax |
.currency | string | Currency code |
.remarks | string | Remarks |
.invoiceInfo:
| Field | Type | Included |
|---|---|---|
.invoiceInfo.invoiceIsFormed | boolean | Whether an invoice exists for the order |
.invoiceInfo.invoiceId | number | Invoice ID |
.invoiceInfo.invoicePrefix | string | Invoice series/prefix |
.invoiceInfo.invoiceNumber | string | Invoice number |
.invoiceInfo.invoiceNumber2 | string | Secondary invoice number |
.invoiceInfo.invoiceDate | date-time | Invoice date |
.invoiceInfo.createdAt | date-time | Invoice creation date/time |
Render Fields
PDF-specific helper fields are available under .render:
| Field | Meaning |
|---|---|
.render.title | Localized document title |
.render.documentNumber | Preferred printable document number |
.render.locale | Current user language |
.render.showExpiryDate | True for quote templates |
.render.amountInWords | Total amount in words |
.render.sumInWords | Compatibility alias for .render.amountInWords |
.render.user.firstName | Rendering user first name |
.render.user.lastName | Rendering user last name |
Company Configuration
Use .render.configuration.company for seller/company details:
{{ .render.configuration.company.name }}
{{ .render.configuration.company.registrationCode }}
{{ .render.configuration.company.vatCode }}
{{ .render.configuration.company.address }}
{{ .render.configuration.company.city }}
{{ .render.configuration.company.phone }}
{{ .render.configuration.company.email }}
{{ .render.configuration.company.website }}
{{ .render.configuration.company.bankDetails }}
{{ .render.configuration.company.logo }}Example:
{{ if .render.configuration.company.logo }}
<img src="{{ .render.configuration.company.logo }}" style="max-height: 60px;">
{{ end }}
<strong>{{ .render.configuration.company.name }}</strong>The header logo and the page background are separate settings:
| Setting | Meaning |
|---|---|
company.logo | Logo rendered by the template HTML |
documents.pdf.background | Full-page PDF background/blank applied behind the rendered PDF |
If documents.pdf.background is empty, no PDF background is applied.
Translations
Use translation keys for visible labels instead of hardcoded text:
{{ T "offer.buyer" }}
{{ T "offer.quantity" }}
{{ T "offer.price" }}
{{ T "offer.subtotal" }}
{{ T "offer.vat" }}
{{ T "offer.total2" }}This keeps one template usable across supported languages.
Formatting Helpers
Common helpers available in templates:
{{ formatDate .date }}
{{ formatDateLithuanian .date }}
{{ formatDatetimeLithuanian .createdAt }}
{{ formatDateUS .date }}
{{ formatDatetimeUS .createdAt }}
{{ formatDateTimeUS .createdAt }}
{{ formatPrice .totalInclTax }}
{{ nl2br .comments }}Date helper output formats:
| Helper | Example output |
|---|---|
formatDate | 2026-05-26 |
formatDateLithuanian | 2026-05-26 |
formatDatetimeLithuanian | 2026-05-26 14:30:00 |
formatDateUS | 05/26/2026 |
formatDatetimeUS | 05/26/2026 02:30 PM |
formatDateTimeUS | 05/26/2026 02:30 PM |
formatDate is an alias for formatDateLithuanian. Use formatDateUS when a US-style MM/DD/YYYY date is needed.
Minimal Example
<h1>{{ .render.title }} {{ .render.documentNumber }}</h1>
<p>{{ formatDate .date }}</p>
<p>
<strong>{{ T "offer.buyer" }}</strong><br>
{{ .companyInfo.companyName }}<br>
{{ .companyInfo.companyCode }}
</p>
<table>
<thead>
<tr>
<th>{{ T "offer.title" }}</th>
<th>{{ T "offer.quantity" }}</th>
<th>{{ T "offer.price" }}</th>
<th>{{ T "offer.subtotal" }}</th>
</tr>
</thead>
<tbody>
{{ range .lineItems }}
<tr>
<td>{{ .title }}</td>
<td>{{ .quantity }} {{ .measurementUnit }}</td>
<td>{{ formatPrice .priceExclTax }} {{ .currency }}</td>
<td>{{ formatPrice .totalExclTax }} {{ .currency }}</td>
</tr>
{{ end }}
</tbody>
</table>
<p>
{{ T "offer.total2" }}:
<strong>{{ formatPrice .totalInclTax }} {{ .currency }}</strong>
</p>