Skip to content

print: placeholder format specifier - whole-number money values print bare (5390, not 5 390.00) #6982

Description

@delchev

The hole

A `.print` template has no way to control how a bound value is formatted. `DataBinder.stringify` applies the money pattern (`### ### ##0.00`, space-grouped, ROOT locale) only to values typed `Double`/`Float`/`BigDecimal`; everything else renders via `toString`.

Both print paths (`PrintEndpoint` for the browser POST, `PrintFacade` for the server-side snapshot mint) parse the `{document, items}` JSON with Gson `ToNumberPolicy.LONG_OR_DOUBLE`, so the presence of a decimal point in the JSON decides the type:

  • `2863.24` → `Double` → `2 863.24` ✔
  • `5390` → `Long` → `5390` ✘

A money value that lands on a whole figure prints bare. The browser path makes this systematic: `JSON.stringify(5390.00)` emits `5390`, so any amount that round-trips through the UI loses its scale marker. On a real invoice this shows as a mixed totals block - amounts with cents formatted, round amounts bare - and there is nothing the template author can write to fix it. Dates have the sibling problem: an ISO `2026-08-29` cannot be reformatted to the local convention (`29.08.2026`).

Blanket-formatting `Long` in the binder would be wrong (an id or a year printed through a template would render `7.00`), so the knob belongs to the template author.

Proposal: an optional `:pattern` suffix on placeholder operands

Mirroring the `{date:yyyyMMdd}` token the declarative `fileName` patterns already use (#6904):

<column width="*" align="right" label="PRICE">{{Price:#,##0.00}}</column>
<text align="right">Date: {{document.Date:dd.MM.yyyy}}</text>
<text>{{document.Customer.NameLocal|document.Customer.Name}}</text>   <!-- unchanged -->

Semantics:

  • the suffix starts at the first `:` of an operand; the pattern applies to that operand's resolved value
  • a `Number` (any subtype, `Long` included) formats via `DecimalFormat` with the existing ROOT + space-grouping symbols - `{{Price:#,##0.00}}` renders `5390` as `5 390.00`
  • a `Temporal`, or a `String` parseable as an ISO date/date-time (the shape the feeders emit), formats via `DateTimeFormatter.ofPattern`
  • anything else - and any pattern that does not fit the value - falls back to today's rendering (lenient by the parser's own contract: a printout never shows raw braces or an exception)
  • `|`-fallback semantics unchanged: blankness is judged on the rendered result, the last operand always renders
  • backward compatible: no existing `.print` template (shipped or generated) carries a `:` inside a placeholder body

Found on a generated sales-invoice print where the items table showed `5390` next to a formatted totals block.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions