From 28b128a85ef23176ea25f03744de305ea3553d79 Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Sun, 21 Jun 2026 18:53:05 +0800 Subject: [PATCH 1/2] =?UTF-8?q?docs(currency):=20document=20the=20field?= =?UTF-8?q?=E2=86=92tenant=20resolution=20chain;=20fix=20currencyMode;=20s?= =?UTF-8?q?howcase=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows the currency-resolution work (#2102/#2119/#2121/#2122). Brings the skills + showcase in line with the now-implemented chain. - objectstack-ui SKILL: document `DatasetMeasure.currency` (ISO 4217) and its resolution order (measure → field `currencyConfig` → tenant default). - objectstack-data field-types: FIX a real doc bug — `currencyMode` was documented as `'multi'/'single'`, the schema is `'fixed'/'dynamic'`; add a "Currency resolution (ADR-0053)" note (field default → tenant default → plain number, never a hardcoded `$`). - objectstack-i18n SKILL: note the workspace regional defaults (timezone / locale / currency) — the `localization` settings, resolved onto ExecutionContext and exposed at `/auth/me/localization`. - showcase: declare a fixed `currencyConfig.defaultCurrency` on field-zoo's currency field (demonstrates + smoke-covers the symbol path); refresh the chart-gallery `budget`/`spent` comment, which claimed an unspecified-currency amount "must not show a $" — now it falls back to the tenant default. Verified: app-showcase builds (field + dataset parse); check:skill-docs in sync. Co-Authored-By: Claude Opus 4.8 --- .../src/datasets/chart-gallery.dataset.ts | 9 +++++---- examples/app-showcase/src/objects/field-zoo.object.ts | 2 +- skills/objectstack-data/rules/field-types.md | 11 +++++++++-- skills/objectstack-i18n/SKILL.md | 5 +++++ skills/objectstack-ui/SKILL.md | 5 +++++ 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/examples/app-showcase/src/datasets/chart-gallery.dataset.ts b/examples/app-showcase/src/datasets/chart-gallery.dataset.ts index 5d41ac7f51..342b0e1afb 100644 --- a/examples/app-showcase/src/datasets/chart-gallery.dataset.ts +++ b/examples/app-showcase/src/datasets/chart-gallery.dataset.ts @@ -41,10 +41,11 @@ export const ShowcaseProjectDataset = defineDataset({ ], measures: [ { name: 'project_count', label: 'Projects', aggregate: 'count' }, - // `budget`/`spent` are currency fields with NO declared currency code, so a - // hardcoded "$" misrepresents the amount (an amount with unspecified - // currency must not show a $ symbol). Use a plain grouped-number format; - // declare a `currency` on the field to get a locale-correct symbol via Intl. + // `budget`/`spent` are currency fields with NO declared currency code. The + // renderer resolves the symbol through the field's `currencyConfig.default- + // Currency` → the tenant `localization.currency` default (ADR-0053); with + // neither set it shows a plain grouped number (never a hardcoded "$"). To + // pin a symbol, set a field `currencyConfig` or a measure `currency`. { name: 'budget_sum', label: 'Total Budget', aggregate: 'sum', field: 'budget', format: '0,0' }, { name: 'spent_sum', label: 'Total Spent', aggregate: 'sum', field: 'spent', format: '0,0' }, ], diff --git a/examples/app-showcase/src/objects/field-zoo.object.ts b/examples/app-showcase/src/objects/field-zoo.object.ts index a405554249..aa29cb168a 100644 --- a/examples/app-showcase/src/objects/field-zoo.object.ts +++ b/examples/app-showcase/src/objects/field-zoo.object.ts @@ -41,7 +41,7 @@ export const FieldZoo = ObjectSchema.create({ // ── Numbers ────────────────────────────────────────────────────────── f_number: Field.number({ label: 'Number', min: 0, max: 1000 }), - f_currency: Field.currency({ label: 'Currency', scale: 2, min: 0 }), + f_currency: Field.currency({ label: 'Currency', scale: 2, min: 0, currencyConfig: { currencyMode: 'fixed', defaultCurrency: 'USD' } }), f_percent: Field.percent({ label: 'Percent', min: 0, max: 100, defaultValue: 50 }), // ── Date & time ────────────────────────────────────────────────────── diff --git a/skills/objectstack-data/rules/field-types.md b/skills/objectstack-data/rules/field-types.md index 163aa155c6..7ab8611b35 100644 --- a/skills/objectstack-data/rules/field-types.md +++ b/skills/objectstack-data/rules/field-types.md @@ -197,12 +197,19 @@ What kind of data? type: 'currency', currencyConfig: { precision: 2, - currencyMode: 'multi', // or 'single' - defaultCurrency: 'USD', + currencyMode: 'fixed', // 'fixed' = one currency for the column; + // 'dynamic' = per-record `{ value, currency }` + defaultCurrency: 'USD', // ISO 4217 }, } ``` +**Currency resolution (ADR-0053).** A displayed amount resolves its symbol +through: the field's own `currencyConfig.defaultCurrency` → the tenant +`localization.currency` default. With neither set, renderers show a plain +grouped number (never a hardcoded `$`). The same chain backs analytics measures +(a measure's explicit `currency` wins over the field/tenant default). + ### Select with Default ```typescript diff --git a/skills/objectstack-i18n/SKILL.md b/skills/objectstack-i18n/SKILL.md index 3a08dc2c29..5d86da329f 100644 --- a/skills/objectstack-i18n/SKILL.md +++ b/skills/objectstack-i18n/SKILL.md @@ -33,6 +33,11 @@ and integration with the I18nService. - You need to **detect missing or stale translations** (coverage analysis). - You are integrating **AI-powered translation suggestions**. - You are implementing **locale-specific formatting** (dates, numbers, currency). + > Workspace regional defaults — reference `timezone`, `locale`, and **`currency`** + > — live in the `localization` SETTINGS (tenant-scoped), are resolved onto every + > request's `ExecutionContext`, and are exposed to the client at + > `GET /api/v1/auth/me/localization`. `localization.currency` is the fallback a + > currency field/measure uses when it omits its own (ADR-0053). - You need to understand **translation file organization strategies** (bundled, per_locale, per_namespace). --- diff --git a/skills/objectstack-ui/SKILL.md b/skills/objectstack-ui/SKILL.md index ddf0a3d72f..283ce5f86e 100644 --- a/skills/objectstack-ui/SKILL.md +++ b/skills/objectstack-ui/SKILL.md @@ -937,6 +937,11 @@ defineDataset({ dimensions: [{ name: 'signed_date', field: 'signed_date', type: 'date', dateGranularity: 'month' }], measures: [{ name: 'signed_count', aggregate: 'count' }], }); +// A monetary measure may declare `currency` (ISO 4217) for a locale-correct +// symbol: `{ name: 'revenue', aggregate: 'sum', field: 'amount', currency: 'USD' }`. +// It resolves measure `currency` → the aggregated field's +// `currencyConfig.defaultCurrency` → the tenant `localization.currency` default +// (ADR-0053). Omit it for non-money measures (count, avg-of-hours). // The widget just selects the dimension by name: { id: 'signed_by_month', type: 'line', From e74da4e66673e9b096be79dfce857b79a472e1b5 Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Sun, 21 Jun 2026 18:58:32 +0800 Subject: [PATCH 2/2] fix(showcase): currencyConfig requires precision (typecheck) --- examples/app-showcase/src/objects/field-zoo.object.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/app-showcase/src/objects/field-zoo.object.ts b/examples/app-showcase/src/objects/field-zoo.object.ts index aa29cb168a..df1bb58d20 100644 --- a/examples/app-showcase/src/objects/field-zoo.object.ts +++ b/examples/app-showcase/src/objects/field-zoo.object.ts @@ -41,7 +41,7 @@ export const FieldZoo = ObjectSchema.create({ // ── Numbers ────────────────────────────────────────────────────────── f_number: Field.number({ label: 'Number', min: 0, max: 1000 }), - f_currency: Field.currency({ label: 'Currency', scale: 2, min: 0, currencyConfig: { currencyMode: 'fixed', defaultCurrency: 'USD' } }), + f_currency: Field.currency({ label: 'Currency', scale: 2, min: 0, currencyConfig: { currencyMode: 'fixed', defaultCurrency: 'USD', precision: 2 } }), f_percent: Field.percent({ label: 'Percent', min: 0, max: 100, defaultValue: 50 }), // ── Date & time ──────────────────────────────────────────────────────