feat(reports): cross-model dimensions + typed per-column filters#6126
Merged
Conversation
…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>
a3b738a to
d2f8243
Compare
`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
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Two report-stack upgrades (stacked on #6125 — the base auto-retargets to
masterwhen it merges):dimensions: [Customer]where Customer is amodel: customersrelation now joins the owning model's real table (INNER JOIN \"CUSTOMERS_CUSTOMER\"on its resolved PK, label field as the display column) viaCrossModelSupport, exactly like the EDM FK resolution. Previously the join used this model's intent-prefixed naming and pointed at a non-existent table. Coversrelation.fieldandfilter(buildWhere) paths too.conditions: [{column, operator, value}], validated against the report's own column aliases + types (FILTER_COLUMNSfrom the.reportcolumns), and wraps the query:SELECT * FROM (QUERY) AS \"REPORT_DATA\" WHERE \"alias\" <op> :reportFilter<i>with typed named parameters. OperatorsEQ/NE/GT/GTE/LT/LTE/LIKE.exportCsv(filter)honors the same conditions; the count subquery gains the PostgreSQL-required alias./exportaccepts the conditions body.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 aPOST /search//countswitch when filters are active.Verification
IntentEngineIT#report_file_stack_generates_typed_column_filters(+ multilingual & full-generate regressions) — green.GTE/LTEranges,LIKEcontains, combined ranges, filtered count and filtered export all correct over HTTP; unknown column → 400; compoundANDreport filter (due <= CURRENT_DATE AND balance > 0) compiles and runs.🤖 Generated with Claude Code