Skip to content

Formatting Values

Values in the quote data are stored in a raw form: prices are plain numbers like 516551, and dates look like 2026-09-17T08:52:37.000000Z. Formatters turn them into something readable.

Add a formatter after the path, separated by a colon. You can chain as many formatters as you like. They are applied from left to right:

{d.name:upperCase}
{d.created_at:formatD('DD MMMM YYYY')}
{d.conf_total:add(d.vat_total):formatN(2)}

Numbers and prices

formatN

formatN(decimals) formats a number with thousands separators and a fixed number of decimals, following the document language:

TagLanguageResult
{d.conf_total:formatN(2)}English (default)516,551.00
{d.conf_total:formatN(2)}{o.lang=da-DK}516.551,00
{d.conf_total:formatN(2)}{o.lang=de-DE}516.551,00
{d.conf_total:formatN(0)}English516,551

Set the language once for the whole document with an option tag, see Language and time zone.

Prices with a currency

Combine formatN with the currency code of the quote:

Total: {d.conf_total:formatN(2)} {d.currencyCode}
Result
Total: 516,551.00 DKK

Or put a fixed currency label in front with prepend, or after with append:

{d.conf_total:formatN(2):prepend('DKK ')}
{d.conf_total:formatN(2):append(' kr.')}
Result
DKK 516,551.00
516,551.00 kr.

Rounding

FormatterExampleResult
round(decimals){d.form_submissions[0].form.data.elements[label='Delivery'].options[0].price:div(3):round(2)}1253.67
ceil3761.2 → ceil3762
floor3761.8 → floor3761
abs-78290 → abs78290

Always round before formatN when the result of a calculation should be rounded in a specific way. See Calculations.

Percentages

Discount inputs are stored as plain numbers, for example 50 for 50 %. Add the sign yourself:

Software discount: {d.form_submissions[0].form.data.elements[label='Software discount in %'].value}%

If you have a ratio between 0 and 1, multiply it first:

{c.discountRatio:mul(100):round(1)}%

Dates

formatD

formatD(pattern) formats a date. All dates in the quote data are in UTC.

TagResult
{d.created_at:formatD('DD-MM-YYYY')}17-09-2026
{d.created_at:formatD('DD/MM/YYYY HH:mm')}17/09/2026 08:52 (UTC, see Time zone)
{d.created_at:formatD('D MMMM YYYY')}17 September 2026
{d.created_at:formatD('dddd D MMMM YYYY'):ucFirst}Thursday 17 September 2026
{d.created_at:formatD('YYYY-MM-DD')}2026-09-17
{d.created_at:formatD('L')}09/17/2026 (English) or 17.09.2026 (Danish)
{d.created_at:formatD('LL')}September 17, 2026 (English) or 17. september 2026 (Danish)

Common pattern tokens:

TokenMeaningExample
YYYY / YYYear2026 / 26
MMMM / MMM / MM / MMonth name, short name, 2-digit, numberSeptember / Sep / 09 / 9
DD / D / DoDay, 2-digit, number, with suffix07 / 7 / 7th
dddd / dddWeekday name, short nameThursday / Thu
HH / mm / ssHours (24h), minutes, seconds08 / 52 / 37
h / AHours (12h), AM/PM8 / AM
WISO week number38
L / LL / LLLShort date, long date, long date with time (depends on language)

Month and weekday names follow the document language set with {o.lang=…}.

Time zone

Dates are stored in UTC. To show local times, set the time zone for the document:

{o.timezone=Europe/Copenhagen}
Created: {d.created_at:formatD('DD-MM-YYYY HH:mm')}
Result
Created: 17-09-2026 10:52

Date calculations

FormatterExampleResult
addD(amount, unit){d.created_at:addD(30, 'day'):formatD('DD-MM-YYYY')}17-10-2026
subD(amount, unit){d.created_at:subD(1, 'month'):formatD('DD-MM-YYYY')}17-08-2026
startOfD(unit){d.created_at:startOfD('month'):formatD('DD-MM-YYYY')}01-09-2026
endOfD(unit){d.created_at:endOfD('month'):formatD('DD-MM-YYYY')}30-09-2026

Units: day, week, month, quarter, year, hour, minute, second.

A typical use is a quote validity date:

This quote is valid until {d.created_at:addD(30, 'day'):formatD('D MMMM YYYY')}.
Result
This quote is valid until 17 October 2026.

Text

FormatterExampleResult
upperCase{d.configuration_name:upperCase}PROJECT NAME
lowerCase{d.email:lowerCase}jane.doe@example.com
ucFirst'hello world' → ucFirstHello world
ucWords{d.name:lowerCase:ucWords}Jane Doe
prepend(text){d.id:prepend('Q-')}Q-2586
append(text){d.id:append('-')}2586-
replace(old, new){d.form_submissions[0].form.name:replace('V2', '')}Migatronic CoWelder
substr(start, end){d.currencyCode:substr(0, 2)}DK
ellipsis(max){d.form_submissions[0].form.data.elements[0].options[0].description:ellipsis(40)}The basic edition of CoWelder combines a...
padl(length, char){d.id:padl(8, '0')}00002586
padr(length, char){d.id:padr(6, '0')}258600
len{d.name:len}8
print(text){d.id:print('Confidential')}Confidential

Quote numbers

Build a quote number from several fields:

Quote no. {d.id:padl(6, '0'):prepend('Q-')}-{d.revision_number}
Result
Quote no. Q-002586-1

Line breaks in descriptions

Option descriptions and text-area request fields can contain line breaks. In Word, use convCRLF to keep them:

{d.form_submissions[0].form.data.elements[label='Solution'].options[0].description:convCRLF}

In HTML templates, use the CSS property white-space: pre-line on the element instead. See HTML templates.

Chaining formatters

Formatters are chained, never nested. Each formatter receives the output of the one before it:

Right: {d.conf_total:add(d.vat_total):formatN(2)}
Wrong: {d.conf_total:formatN(add(d.vat_total))}

Order matters. Calculate first, then format. formatN turns a number into text, so a calculation after it will not work as expected.

Formatter reference

CategoryFormatters
NumbersformatN, round, ceil, floor, abs, add, sub, mul, div, mod
DatesformatD, addD, subD, startOfD, endOfD, diffD
TextupperCase, lowerCase, ucFirst, ucWords, prepend, append, replace, substr, split, ellipsis, padl, padr, len, print, convCRLF, unaccent
ListsarrayJoin, arrayMap, aggStr, aggStrD
ConditionsifEQ, ifNE, ifGT, ifGTE, ifLT, ifLTE, ifIN, ifNIN, ifEM, ifNEM, show, elseShow, drop, keep, showBegin/showEnd, hideBegin/hideEnd
TotalsaggSum, aggAvg, aggMin, aggMax, aggCount, aggCountD, cumSum, cumCount
Images and codesimageFit, barcode
Storing valuesset