From b22f99e90d44ca0aa10edfce048aa26528afeb7c Mon Sep 17 00:00:00 2001 From: delchev Date: Fri, 31 Jul 2026 15:06:33 +0300 Subject: [PATCH] Propose print-template row filtering (filter/match) --- .../0002-print-template-row-filtering.md | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 proposals/0002-print-template-row-filtering.md diff --git a/proposals/0002-print-template-row-filtering.md b/proposals/0002-print-template-row-filtering.md new file mode 100644 index 0000000..8437c18 --- /dev/null +++ b/proposals/0002-print-template-row-filtering.md @@ -0,0 +1,85 @@ +# Print-template row filtering (`filter` / `match`) + +- **Status:** draft +- **Issue:** +- **Proposed for:** the printable-documents template language (versions/1.1.md "Printable documents") + +## The problem + +A print template's `table` renders **every** element of the collection it is bound to, and `if` +tests only the truthiness of a single path. One collection therefore cannot render into several +purpose-grouped tables - yet that is the shape of the most ordinary financial documents: + +- a **payslip** prints earnings in one column and deductions in the other, both from the same + fiche-line items; +- a **journal entry** prints its debit side next to its credit side, both from the same line rows; +- an **invoice VAT summary** groups the same items per rate. + +The line items of such a document carry a discriminator field (a line kind, a side, a rate group), +but the template language has no way to say "this table shows only the rows whose kind is X". +Today the only options are pre-splitting the data feed (the generated feeders deliberately feed +the entity's items as ONE collection) or abandoning the grouped layout. + +## The proposed shape + +Two attributes, valid on `table` (and the row-expanding `for`), plus `match` on `if`: + +```text + + + Earnings + + {{Name}} + {{Amount}} +
+
+ + Deductions + + {{Name}} + {{Amount}} +
+
+
+ + + Final document + +``` + +- `filter=""` on a `table`/`for` names a path resolved **in each row's own scope**. + Without `match`, a row is kept when the resolved value is truthy. +- `match="A | B"` lists accepted literal values, `|`-separated, surrounding whitespace trimmed. + A row is kept when the resolved value's string form equals one of the literals. +- `match` on an `if` compares the node's resolved `source` against the listed literals instead + of testing truthiness. + +## Expected behaviour + +- A filtered `table` renders its column definitions unchanged and one row per KEPT element, in + source order; elements failing the filter are skipped entirely (no empty rows). +- A filtered `for` expands its children only for the kept elements. +- An unresolved / null filter value never matches a `match` list, and is falsy without one. +- Value comparison is by the value's plain string form - numbers compare by their canonical + rendering, booleans as `true`/`false`. No coercion beyond that, no operators, no expressions: + the language stays a declarative value match. +- A template written with `filter`/`match` and rendered by a generator that predates them MUST + degrade to rendering all rows (unknown attributes are ignored), never fail to parse. + +## Edge rules + +- `filter` absent: the table/for renders every element (today's behaviour, unchanged). +- `match` present without `filter` on a `table`/`for`: no effect (there is nothing to compare); + conforming implementations should ignore it. +- Empty `match` (blank string) is treated as absent: plain truthiness of the filtered path. +- A literal containing `|` is not expressible; the discriminators this feature targets are + code-like line kinds, which never contain pipes. +- `match` on `if` without a `source` keeps the children hidden (a null never matches). + +## Prior art / workarounds + +Hand-maintained pre-split collections in a custom data feeder, or a single mixed table with a +kind column - both give up the standard two-column financial layout. Report-side grouping does +not help: the print template renders a document's OWN items, not a report. The reference +implementation ships the behaviour in its template data-binding layer with parser-level +compatibility (the attributes parse as plain strings like every other attribute).