Skip to content

feat(reports): cross-model dimensions + typed per-column filters#6126

Merged
delchev merged 4 commits into
feat/intent-multilingualfrom
feat/report-column-filters
Jul 3, 2026
Merged

feat(reports): cross-model dimensions + typed per-column filters#6126
delchev merged 4 commits into
feat/intent-multilingualfrom
feat/report-column-filters

Conversation

@delchev

@delchev delchev commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

What

Two report-stack upgrades (stacked on #6125 — the base auto-retargets to master when it merges):

  1. Cross-model report dimensionsdimensions: [Customer] where Customer is a model: customers relation now joins the owning model's real table (INNER JOIN \"CUSTOMERS_CUSTOMER\" on its resolved PK, label field as the display column) via CrossModelSupport, exactly like the EDM FK resolution. Previously the join used this model's intent-prefixed naming and pointed at a non-existent table. Covers relation.field and filter (buildWhere) paths too.
  2. Typed per-column report filters, applied server-side — pagination, count and CSV export all reflect them:
    • dao template: the generated report repository accepts conditions: [{column, operator, value}], validated against the report's own column aliases + types (FILTER_COLUMNS from the .report columns), and wraps the query: SELECT * FROM (QUERY) AS \"REPORT_DATA\" WHERE \"alias\" <op> :reportFilter<i> with typed named parameters. Operators EQ/NE/GT/GTE/LT/LTE/LIKE. exportCsv(filter) honors the same conditions; the count subquery gains the PostgreSQL-required alias.
    • rest template: unknown column/operator → 400; /export accepts the conditions body.
    • Harmonia report page: generation-time typed column metadata (reportColumns: [{key, kind: date|number|boolean|text}]) renders a filter panel — date ranges, number ranges, boolean select, text contains — with Apply/Clear, an active-count badge on the toolbar toggle, and a POST /search//count switch when filters are active.

Verification

  • IntentEngineIT#report_file_stack_generates_typed_column_filters (+ multilingual & full-generate regressions) — green.
  • Live on the multi-model sample's four new invoice reports (sample PR follows): cross-model joins return customer/product names; GTE/LTE ranges, LIKE contains, combined ranges, filtered count and filtered export all correct over HTTP; unknown column → 400; compound AND report filter (due <= CURRENT_DATE AND balance > 0) compiles and runs.

🤖 Generated with Claude Code

delchev and others added 2 commits July 2, 2026 20:29
…ver-side)

Two report-stack upgrades driven by the multi-model sample:

- ReportIntentGenerator joins a CROSS-MODEL relation dimension
  (dimensions: [Customer] where Customer is model: customers) against the
  owning model's real table / primary-key column / label field, resolved
  via CrossModelSupport exactly like the EDM FK (leaf-first, loud failure).
  Previously the join used this model's intent-prefixed naming and pointed
  at a non-existent local table; relation.field and filter (buildWhere)
  paths are covered too.
- The generated report stack gains typed per-column filters applied
  SERVER-side, so pagination, count and CSV export all reflect them:
  - dao template (reportFileEntity): accepts conditions
    [{column, operator, value}] validated against the report's own column
    aliases + types (FILTER_COLUMNS from $columns) and wraps the query -
    SELECT * FROM (QUERY) AS "REPORT_DATA" WHERE "alias" <op> :reportFilter<i>
    with typed named parameters; operators EQ/NE/GT/GTE/LT/LTE/LIKE;
    exportCsv(filter) honors the same conditions; the count subquery gains
    the PostgreSQL-required alias.
  - rest template: /search, /count and /export map an unknown
    column/operator (IllegalArgumentException) to 400; /export accepts the
    conditions body.
  - Harmonia report page: generation-time typed column metadata
    (reportColumns [{key, kind: date|number|boolean|text}] from the report
    columns) renders a filter panel - date ranges, number ranges, boolean
    select, text contains - with Apply/Clear, an active-count badge on the
    toolbar toggle, and a POST /search + /count switch when filters are on.

Tests: IntentEngineIT.report_file_stack_generates_typed_column_filters.
Verified live on the multi-model sample's four new invoice reports:
cross-model joins return customer/product NAMES ("Acme Ltd", not FK ids);
GTE/LTE ranges, LIKE contains, combined ranges, filtered count and export
all correct over HTTP; unknown column -> 400.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sitive FS)

The generation service derives genFolderName from the report file name in
its ORIGINAL case ("OrdersByCustomer"), while the Java files use the
sanitized javaGenFolderName ("ordersbycustomer") - two distinct folders on
a case-sensitive filesystem. The IT asserted both under the sanitized name,
which only passed on macOS where the two paths collapse into one folder.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@delchev delchev force-pushed the feat/report-column-filters branch from a3b738a to d2f8243 Compare July 2, 2026 17:30
delchev and others added 2 commits July 2, 2026 20:37
`dimensions: ["month(date)"]` groups a date for aggregation as a sortable
YYYYMM integer ((EXTRACT(YEAR) * 100 + EXTRACT(MONTH)) - e.g. 202607);
`year(field)` emits the plain year. Enables monthly income/VAT reports:

    - name: MonthlyRevenue
      source: SalesInvoice
      dimensions: ["month(date)"]
      measures: ["count(*)", "sum(net)", "sum(vat)", "sum(total)"]

The YYYYMM integer pairs naturally with the number-range column filters.
Standard-SQL EXTRACT (H2/PostgreSQL; SQL Server lacks EXTRACT - documented
limitation). Covered by the OrdersByMonth assertions in IntentEngineIT;
verified live on the multi-model sample (June/July buckets with correct
net/VAT/total sums, month >= 202607 range filter).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…m the .report model

The .report model now carries per-column rendering metadata (single source
for every report UI): numeric columns (INTEGER/BIGINT/DECIMAL) get
`align: "right"`, decimals additionally the platform money pattern
(`### ### ### ##0.00`). ReportIntentGenerator emits them; the Harmonia
report-file template derives the same defaults from the column types for
.report files that predate the metadata.

Consumers:
- report-file page: headers + cells right-align via the metadata; decimal
  cells format by the DecimalFormat-style pattern (grouped thousands, fixed
  decimals - the document totals formatter); pattern-less numeric columns
  (counts, year/month buckets) render as clean integers instead of the
  DB's 1.0 / 202607.0.
- in-SPA EDM report table: same alignment + formatter, resolved at
  generation time from the property types / formatPattern.

Covered by the align/pattern assertions in IntentEngineIT; verified live on
MonthlyRevenue (five right-aligned columns, patterned sums, clean buckets).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@delchev delchev merged commit 7376595 into feat/intent-multilingual Jul 3, 2026
11 of 17 checks passed
@delchev delchev deleted the feat/report-column-filters branch July 3, 2026 06:44
delchev added a commit that referenced this pull request Jul 3, 2026
…le seeds, Region & Language flag (#6125)

* feat(intent): multi-language data — Java Translator port, _LANG generation, language/file seeds, Harmonia Region & Language

Ports the TS-era `multilingual` entity feature to the Java stack and exposes
it through the intent DSL, with one per-user language flag driving frontend
and backend.

- SDK: new org.eclipse.dirigible.sdk.db.Translator (api-modules-java) — the
  db/translator.ts port with a name-based merge: reads <TABLE>_LANG
  (GUID, Id, <PascalCase property columns>, Language — the codbex-uoms-data
  convention), matches columns to entity fields case-insensitively (reads the
  whole table and filters in memory, so it is independent of identifier
  casing/quoting), normalizes locale tags to the primary subtag, tolerates a
  missing table and no-ops without a language.
- DAO template: a multilingual="true" entity overrides ALL read paths —
  findById, findOne (the generated controller reads single records through
  findOne; findById alone missed GET-by-id, caught live), findAll x3, query —
  overlaying translations for the thread-bound Accept-Language
  (User.getLanguage(); listeners/jobs read base values). TS-parity caveat
  documented: editing under a non-base language saves displayed values.
- Schema template: emits <TABLE>_LANG per multilingual model (string-typed
  non-PK/FK/calculated/audit properties, columns named after the PROPERTY,
  Language VARCHAR(2)); own loop before the base tables so the existing
  comma logic stays untouched.
- Intent DSL: entity `multilingual: true` -> EDM attribute (same one the EDM
  editor writes); seeds gain `language: bg` (translation rows -> _LANG csvim
  with auto-numbered GUIDs) and `file: data/x.csv` (large data sets reference
  an authored CSV — only the .csvim is generated; the path must be in a
  subfolder since root-level .csv files are intent-owned and scrubbed);
  top-level `languages: [en, bg]` -> .model root -> Harmonia config.js.
- Harmonia: shared locale Alpine store (localStorage codbex.harmonia.language)
  + Region & Language picker in the generated Settings page (mirrors the IDE
  settings-locale view); the shared fetch client sends the value as
  Accept-Language on every call; the document Print flow prefers the
  configured language. Harmonia itself has no i18n API (verified 1.24.2) —
  UI-label i18n stays a follow-up on top of the store.
- Docs: intent-assistant-guide, engine-intent/root/engine-java CLAUDE.md
  (the aspirational "repository carries multi-language support" claim now
  describes the real mechanism), Harmonia README.

Tests: Translator-stack IntentEngineIT (multilingual_entity_generates_the_
translation_stack) + IntentParserTest/EdmIntentGeneratorTest/
CsvimIntentGeneratorTest additions. Verified live on the adapted
sample-intent-multi-model uoms: list + by-id reads translate under
Accept-Language: bg, base values without it, untranslated rows fall back;
countries load from an authored data/countries.csv file seed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: format engine-intent sources (formatter cache had masked violations)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(reports): cross-model dimensions + typed per-column filters (#6126)

* feat(reports): cross-model dimensions + typed per-column filters (server-side)

Two report-stack upgrades driven by the multi-model sample:

- ReportIntentGenerator joins a CROSS-MODEL relation dimension
  (dimensions: [Customer] where Customer is model: customers) against the
  owning model's real table / primary-key column / label field, resolved
  via CrossModelSupport exactly like the EDM FK (leaf-first, loud failure).
  Previously the join used this model's intent-prefixed naming and pointed
  at a non-existent local table; relation.field and filter (buildWhere)
  paths are covered too.
- The generated report stack gains typed per-column filters applied
  SERVER-side, so pagination, count and CSV export all reflect them:
  - dao template (reportFileEntity): accepts conditions
    [{column, operator, value}] validated against the report's own column
    aliases + types (FILTER_COLUMNS from $columns) and wraps the query -
    SELECT * FROM (QUERY) AS "REPORT_DATA" WHERE "alias" <op> :reportFilter<i>
    with typed named parameters; operators EQ/NE/GT/GTE/LT/LTE/LIKE;
    exportCsv(filter) honors the same conditions; the count subquery gains
    the PostgreSQL-required alias.
  - rest template: /search, /count and /export map an unknown
    column/operator (IllegalArgumentException) to 400; /export accepts the
    conditions body.
  - Harmonia report page: generation-time typed column metadata
    (reportColumns [{key, kind: date|number|boolean|text}] from the report
    columns) renders a filter panel - date ranges, number ranges, boolean
    select, text contains - with Apply/Clear, an active-count badge on the
    toolbar toggle, and a POST /search + /count switch when filters are on.

Tests: IntentEngineIT.report_file_stack_generates_typed_column_filters.
Verified live on the multi-model sample's four new invoice reports:
cross-model joins return customer/product NAMES ("Acme Ltd", not FK ids);
GTE/LTE ranges, LIKE contains, combined ranges, filtered count and export
all correct over HTTP; unknown column -> 400.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(test): report UI files live under the RAW genFolderName (case-sensitive FS)

The generation service derives genFolderName from the report file name in
its ORIGINAL case ("OrdersByCustomer"), while the Java files use the
sanitized javaGenFolderName ("ordersbycustomer") - two distinct folders on
a case-sensitive filesystem. The IT asserted both under the sanitized name,
which only passed on macOS where the two paths collapse into one folder.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(reports): month()/year() date-bucket dimensions

`dimensions: ["month(date)"]` groups a date for aggregation as a sortable
YYYYMM integer ((EXTRACT(YEAR) * 100 + EXTRACT(MONTH)) - e.g. 202607);
`year(field)` emits the plain year. Enables monthly income/VAT reports:

    - name: MonthlyRevenue
      source: SalesInvoice
      dimensions: ["month(date)"]
      measures: ["count(*)", "sum(net)", "sum(vat)", "sum(total)"]

The YYYYMM integer pairs naturally with the number-range column filters.
Standard-SQL EXTRACT (H2/PostgreSQL; SQL Server lacks EXTRACT - documented
limitation). Covered by the OrdersByMonth assertions in IntentEngineIT;
verified live on the multi-model sample (June/July buckets with correct
net/VAT/total sums, month >= 202607 range filter).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(reports): right-align numeric columns and format by pattern, from the .report model

The .report model now carries per-column rendering metadata (single source
for every report UI): numeric columns (INTEGER/BIGINT/DECIMAL) get
`align: "right"`, decimals additionally the platform money pattern
(`### ### ### ##0.00`). ReportIntentGenerator emits them; the Harmonia
report-file template derives the same defaults from the column types for
.report files that predate the metadata.

Consumers:
- report-file page: headers + cells right-align via the metadata; decimal
  cells format by the DecimalFormat-style pattern (grouped thousands, fixed
  decimals - the document totals formatter); pattern-less numeric columns
  (counts, year/month buckets) render as clean integers instead of the
  DB's 1.0 / 202607.0.
- in-SPA EDM report table: same alignment + formatter, resolved at
  generation time from the property types / formatPattern.

Covered by the align/pattern assertions in IntentEngineIT; verified live on
MonthlyRevenue (five right-aligned columns, patterned sums, clean buckets).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant