file-wordDocument Templating

Getting Started

Realpad CRM can generate documents (contracts, invoices, protocols, etc.) from Word templates. You create a .docx file with special placeholder markers, upload it to the system, and the CRM fills in the real data automatically.

The system uses XDocReportarrow-up-right with the Apache Velocityarrow-up-right template engine under the hood. You don't need to understand these libraries in depth β€” this guide covers everything you need to create templates.


Document types & their placeholders documentation


Step-by-Step: Creating a Template

1

Open Microsoft Word

Create or open a .docx file. The template is a normal Word document β€” you can use all standard formatting (bold, tables, headers, page breaks, etc.).

2

The recommended way to insert placeholders is through Word Merge Fields. This prevents a common issue where Word silently splits your placeholder text across internal formatting runs, breaking it (see Common Pitfalls for details).

How to insert a Merge Field:

  1. Place your cursor where you want the placeholder.

  2. Press Ctrl+F9 (Windows) or Cmd+F9 (Mac) to insert a field β€” you will see gray braces { }.

  3. Inside the field braces, type: MERGEFIELD $placeholderName (for example: MERGEFIELD $dealId), or MERGEFIELD $placeholderName \* MERGEFORMAT.

  4. Press Alt+F9 (or Shift+F9 on Mac) to update the field display β€” it will now show the placeholder name as a preview.

circle-exclamation
3

Placeholders Typed Directly (forbidden)

While it is in theory possible to insert placeholders directly into the document, this breaks our ability to inspect them, and thus is forbidden.

4

Upload and Test

  1. Upload the template to the CRM (in the Document Templates section)

  2. Generate a test document for a real Deal to verify all placeholders are filled in correctly

  3. Adjust formatting and placeholders as needed


Placeholder Syntax

Simple Placeholders

A simple placeholder starts with $ followed by the name:

Accessing Properties of Objects

Inside a loop, each item is an object with multiple properties. Use a dot . to access them:

This also works for other objects β€” for example, units inside a #foreach($unit in $units) loop:

Loops (Repeating Content)

To repeat a section for each item in a list, use #foreach ... #end:

This generates one block of text for each Unit on the Deal. Everything between #foreach and #end is repeated.

Loops in tables: A common pattern is to put the #foreach and #end in the first column of a table row. The entire row gets repeated for each item:

#
Unit
Type
Price

#foreach($unit in $units) $foreach.count

$unit.internalId

$unit.type

$unit.finalPriceVat #end

Note: $foreach.count is a built-in Velocity variable that gives you the current iteration number (1, 2, 3, ...). You can also use $foreach.index for zero-based numbering (0, 1, 2, ...) and $foreach.hasNext to check if there are more items.

Conditional Content

To show content only when a condition is true, use #if ... #end:

You can also use #else and #elseif:

Checking if a value exists: Many placeholders come with a boolean companion (prefixed with is). Use it to check before displaying:

You can also use the $Utils.isSet() helper to check any value:

Comparing values: You can compare numbers and strings inside #if:

Note: To compare numbers, use .getAsNumber() first β€” otherwise you'd be comparing the formatted text, not the numeric value.

Variables

You can define temporary variables with #set to store computed values or simplify repeated expressions:

Nested Loops

Loops can be nested. For example, to list defects for each room during an inspection:

Velocity Reference

The template engine is Apache Velocityarrow-up-right. The most commonly used features are covered in this guide, but for the full syntax reference, see the Velocity User Guidearrow-up-right. The key directives are #if/#elseif/#else/#end, #foreach/#end, and #set.


Data Types and Methods

Every placeholder has a data type that determines how it is displayed and what methods you can call on it.

String

Plain text values. Rendered as-is in the document.

Cyrillic Transliteration (RS developers only): For Serbian (RS) developers, String placeholders support .toCyrillic(). This is typically used inside a #foreach loop:

Important: .toCyrillic() is only available for RS developers. For all other developers, String values are plain text and calling .toCyrillic() will output the method call literally. Templates that use .toCyrillic() should only be used by RS developers.

DateWrapper

Formatted dates that respect the user's locale settings.

Available methods:

Method
Description
Example

.format("pattern")

Custom date format using Joda-Time patternsarrow-up-right

$createdOn.format("dd. MMMM yyyy") β†’ "23. December 2025"

.addDays(n)

Add days to the date

$createdOn.addDays(14) β†’ date + 14 days

.addWeeks(n)

Add weeks to the date

$createdOn.addWeeks(2) β†’ date + 2 weeks

.addMonths(n)

Add months to the date

$createdOn.addMonths(3) β†’ date + 3 months

.isSet()

Check if date has a value

#if($createdOn.isSet()) ... #end

Common date format patterns:

Pattern
Result

dd.MM.yyyy

23.12.2025

d. MMMM yyyy

23. December 2025

dd/MM/yyyy

23/12/2025

yyyy-MM-dd

2025-12-23

EEEE, d. MMMM yyyy

Tuesday, 23. December 2025

Note: The month and day names (e.g. "December", "Tuesday") are automatically localized to the user's language.

NumberWrapper

Formatted numbers that respect the user's locale (decimal separator, thousands grouping, currency symbol).

Available methods:

Method
Description
Example

.add(number)

Add to the value

$dealPriceVAT.add($otherPrice)

.sub(number)

Subtract from the value

$dealPriceVAT.sub($discount)

.mul(number)

Multiply the value

$unit.area.mul(2)

.div(number)

Divide the value

$dealPriceVAT.div(2)

.round()

Round to whole number

$dealPriceVAT.round()

.roundFloor()

Round down to whole number

$dealPriceVAT.roundFloor()

.toInt()

Convert to integer (truncate)

$unit.area.toInt()

.strip()

Remove trailing zeros from decimals

$dealPriceVAT.strip() β†’ "1 250 000 CZK"

.keep()

Always show decimal places

$dealPriceVAT.keep() β†’ "1 250 000,00 CZK"

.dash()

Show dashes instead of trailing zeros

$dealPriceVAT.dash() β†’ "1 250 000,β€” CZK"

.cz()

Number written in Czech words

$dealPriceVAT.cz()

.sk()

Number written in Slovak words

$dealPriceVAT.sk()

.isSet()

Check if number has a value

#if($dealPriceVAT.isSet()) ... #end

.getAsNumber()

Raw number for comparisons

#if($dealPriceVAT.getAsNumber() > 0) ... #end

Tip: Many number placeholders also come with pre-computed _cz and _sk word variants as separate placeholders (e.g. $dealPriceVAT_cz). These are equivalent to calling .cz() on the base placeholder.

Arithmetic: Methods accept both other placeholders and plain numbers, and can be chained:

Boolean

True/false values, used in #if conditions:

$Utils Helper Object

A utility object (DealDocumentUtils) available in all document templates β€” Deal Documents, Invoices, Reclamation Protocols, and others.

Method
Description
Example

$Utils.isSet(value)

Check if any value is set (works with all types)

#if($Utils.isSet($mortgageAmount))

$Utils.sum(n1, n2, ...)

Sum multiple NumberWrapper values

$Utils.sum($unit.area, $unit.areaBalcony)

$Utils.wrap(number)

Create a NumberWrapper from a raw long or double

$Utils.wrap(100)

$Utils.isSet() returns false for null values, empty collections, numbers/dates that have no value, and missing images. It is the most reliable way to check whether a placeholder has data.

Usage example:

Method Chaining

Both DateWrapper and NumberWrapper methods return new wrapper objects, so you can chain multiple operations:


Embedding Images

Some placeholders provide images (for example, Unit floor plans or Inspection defect photos). These work similarly to text placeholders but require a different insertion method in the Word template β€” you use bookmarks instead of Merge Fields or typed text.

How to add an image placeholder

  1. Insert a placeholder image into your Word document (any small image will do β€” it serves as a sizing reference)

  2. Select the image

  3. Go to Insert β†’ Bookmark

  4. In the Bookmark name field, type the image placeholder name β€” but replace dots with underscores (for example, for the placeholder $unit.plan, type unit_plan)

  5. Click Add to create the bookmark

  6. Resize the placeholder image to the approximate dimensions you want the real image to have in the generated document β€” the generated image will match this size

When the document is generated, the bookmarked placeholder image is replaced with the actual image from the CRM. If no image is available for a given placeholder, the placeholder image is removed from the output (it will not appear as a broken image).

Important: Bookmark names cannot contain dots (.) or dollar signs ($). Always replace dots with underscores: $unit.plan β†’ bookmark name unit_plan, $defect.image β†’ bookmark name defect_image.

Image placeholders in loops

Images work inside #foreach loops too. For example, to show a photo for each inspection defect, you would insert a placeholder image inside the loop body and bookmark it with the image field name (using underscores). The image is then replaced for each iteration of the loop.

Available image placeholders

Image placeholders are listed in the placeholder reference for each document type. Look for placeholders marked as Image type. Common examples include floor plan images on Units and defect attachment photos on Inspection documents.


Common Pitfalls

A best practice to investigate a problematic template, especially if it is based on a working one, is to split it approximately in half, remove one half and:

  • If the smaller part works, the problem was in the other part.

  • If the smaller part doesn't work, the problem is in it.

In both cases, split down the problematic part further, until you discover the problematic placeholder or control structure.

chevron-right1. Broken Placeholders (Word Formatting Runs)hashtag

The most common issue. Word internally splits text into "runs" based on formatting. If you type $dealId and then go back to fix a typo or apply formatting, Word may store it internally as $deal + Id in separate runs. The template engine then cannot recognize the placeholder.

How to fix:

  • Select the entire placeholder text, cut it, and paste it back using Paste as Plain Text (Ctrl+Shift+V)

  • Or use Merge Fields instead (see Step 2 β€” Insert Placeholders Using Merge Fields)

  • Or type the placeholder in one go without going back to edit it

How to diagnose: If a placeholder appears literally in the generated document (e.g., you see $dealId instead of a number), this is almost certainly the cause.

chevron-right2. Missing `$` Signhashtag

Every placeholder must start with $. If you write dealId instead of $dealId, it will appear as literal text.

chevron-right3. Unclosed `#foreach` or `#if` Blockshashtag

Every #foreach needs a matching #end, and every #if needs a matching #end. A missing #end will cause the template generation to fail.

Example:

WRONG:

CORRECT:

chevron-right4. Using a Placeholder That Doesn't Existhashtag

If you use a placeholder name that the system doesn't recognize, it will appear literally in the generated document (e.g., $nonExistentField). Double-check the name against the placeholder reference for your document type.

chevron-right5. Placeholder from Wrong Document Typehashtag

Each document type has its own set of available placeholders. For example, Invoice-specific placeholders like $paymentType are not available in Reclamation documents. Check which document type you are creating the template for.

chevron-right6. Loops Inside Tableshashtag

When using #foreach in a table, the #foreach and #end tags must be in the same table row. Placing #foreach in one row and #end in another will cause unexpected results.

chevron-right7. Whitespace and Newlineshashtag

#foreach, #if, and #end directives take up a line in the output. If you don't want extra blank lines, place them on the same line as adjacent content, or put them at the start/end of a paragraph.


Quick Example

Here is a minimal Deal Document template:

A more complete example using the $customers list:


Last updated