Table Calculations
AdviceDocs supports arithmetic and a small set of helper functions inside {{ ... }} placeholders. This is most useful in tables, where each row often needs a derived value (a line total, a percentage, a row that aggregates a column) and the data extraction step doesn’t pre-compute it. The renderer wraps numeric fields in a smart number type that handles addition, multiplication, division, and so on even when the underlying value arrives as a string.
What works inside {{ ... }}
Arithmetic
All the standard binary operators work: +, -, *,/, // (floor division), % (modulo), ** (exponent). String values that look like numbers are auto-converted, so you don’t have to worry about whether price arrived as "1234.50" or 1234.5.
{{ price * quantity }}
{{ amount + extra }}
{{ end_balance - start_balance }}
{{ total / count }}
{{ amount * 1.1 }} (add 10%)
{{ amount * (1 - 0.075) }} (subtract 7.5%)Division by zero is safe
{{ value / 0 }} renders as 0. The smart number wrapper catches the divide and returns zero rather than failing the whole template render.The sum() function
AdviceDocs exposes a custom sum() function that handles three patterns:
{{ sum(numbers) }}— sum a flat list of numbers.{{ sum(items, 'attribute') }}— sum one attribute across a list of objects.{{ sum(items, 'nested.attribute') }}— nested attribute paths work too.
{{ sum(asset_values) }}
{{ sum(client.investments, 'balance') }}
{{ sum(accounts, 'portfolio.value') }}
Total: {{ sum(products, 'value') }}Other helper functions
len(items)— count the items in a list.min(a, b),max(a, b)— smallest / largest of the arguments (or of a list).round(x, n)— round tondecimal places.abs(x)— absolute value.int(x),float(x)— coerce a value into the matching numeric type.
Ternary expressions
Inline conditionals using Jinja2’s ternary syntax work:
{{ "Yes" if approved else "No" }}
{{ recommended_amount if has_recommendation else 0 }}
{{ price * 0.9 if is_member else price }}Comparisons
You can write a comparison inside a placeholder; it renders as True or False. In practice, this is usually clearer wrapped in a ternary ({{ "Yes" if balance > 0 else "No" }}).
Putting calculations in tables
The most common use case is a per-row line total (e.g. value × weight) and a footer row with the column total. Use a regular table-row loop with arithmetic in the cells, then a separate row outside the loop with sum().
| Asset | Value | Weight | Allocation |
|-------|-------|--------|------------|
{% tr for asset in assets %}
| {{ asset.name }} | {{ asset.value }} | {{ asset.weight }} | {{ asset.value * asset.weight }} |
{% endtr %}
| **Total** | {{ sum(assets, 'value') }} | | {{ sum(assets, 'value') * 1.0 }} |{% tr for product in products %}
| {{ product.name }} | {{ product.value }} | {{ round(product.value / sum(products, 'value') * 100, 1) }}% |
{% endtr %}{% tr for account in accounts %}
| {{ account.name }} | {{ account.balance }} | {{ "High" if account.balance > 100000 else "Standard" }} |
{% endtr %}Auto-formatting
Calculation results are automatically formatted with thousands separators. So {{ 1000000 }} renders as 1,000,000, and {{ price * quantity }} when the result is 123456.78 renders as 123,456.78.
Field values are NOT auto-formatted
{{ account_balance }} renders the value as it arrived from the data layer — no commas, no currency symbol. To force formatting, multiply by 1 ({{ account_balance * 1 }}) or pre-format the value upstream.What does not work
- Filters —
{{ value | upper }},{{ amount | round(2) }},{{ items | length }}all silently break (the parser strips the filter and only the field name is extracted; the renderer either ignores the filter or fails). Use a function call instead:{{ round(amount, 2) }},{{ len(items) }}. - String concatenation with
+—{{ "$" + amount }}doesn’t work because+is numeric. Put literal text outside the braces instead:${{ amount }}. - Method calls —
{{ date.strftime("%Y") }},{{ name.upper() }}. Pre-format on the data side. - Running totals inside a loop — AdviceDocs doesn’t support
{% set %}, so you can’t accumulate a running sum across iterations. Usesum()in a footer row, or pre-compute the totals in extraction. - Date arithmetic —
{{ end_date - start_date }}doesn’t produce a meaningful timedelta. Compute the difference upstream and reference it as a numeric field. - Custom Python functions — only the helpers listed above are exposed. There’s no way to register your own.
When to pre-compute instead
In-template calculations are powerful but they don’t make it into the field list. If you want a value to appear in the field configuration UI — with a description, a manual type override, condition dependencies, or LLM-driven extraction — pre-compute it on the data side and reference it as a regular field.
{{ total_assets }} (pre-computed in extraction)
{{ recommended_contribution }} (pre-computed)
{{ projection_year_5_balance }} (pre-computed)Common pitfalls
Strings with leading zeros are converted to numbers
"007" becomes 7 when used in arithmetic. Account numbers, postcodes, and other identifiers should be referenced without operators ({{ account_number }}) so they stay strings.Filter syntax silently disappears
{{ amount | round(2) }} looks valid but doesn’t do what you expect: the filter is stripped at parse time and the renderer falls through. Use the function form {{ round(amount, 2) }} instead.Use sum() over manual loops
{% for %}. Use {{ sum(items, 'value') }} in the footer row of the table — it evaluates against the same collection the loop iterates over.Multiply by 1 to force number formatting
{{ field_name * 1 }}. The arithmetic wrapper kicks in and the result is auto-formatted.Syntax to copy
{{ price * quantity }}
{{ amount + extra }}
{{ total / count }}
{{ amount * 1.1 }}
{{ round(amount, 2) }}{{ sum(values) }}
{{ sum(items, 'value') }}
{{ sum(accounts, 'portfolio.balance') }}{{ "Yes" if approved else "No" }}
{{ price * 0.9 if is_member else price }}
{{ recommended_amount if has_recommendation else 0 }}{{ account_balance * 1 }}
{{ portfolio_value * 1 }}| Asset | Value | Weight | Line Total |
|-------|-------|--------|------------|
{% tr for asset in assets %}
| {{ asset.name }} | {{ asset.value }} | {{ asset.weight }} | {{ asset.value * asset.weight }} |
{% endtr %}
| **Total** | {{ sum(assets, 'value') }} | | |