You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 48bffe3
Browse filesBrowse the repository at this point in the historyBrowse files
fix(studio): the dataset inspector stores no guessed measure currency (#27208)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8Kb1Q1LciCB4G4onVaiMJWK)_Fixes#27156
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么:** Studio 数据集检查器里选「货币」格式但不选币种时,不再往度量上写 `currency:
'USD'`;货币字段上的度量默认「继承」,保存的度量没有 `currency` 键。
- **为什么改:** 度量自带的 `currency` 优先于字段固定币种和组织默认币种,猜出来的 USD 让人民币组织的仪表盘显示
`$`(裁决 6464202770,Q2 = X:任何地方都不猜币种)。
- **风险与回滚:** 只影响检查器的写入与读回;已存的 `currency: 'USD'` 原样保留、不迁移。回滚 = revert 本
PR。
- **先看:** `DatasetDefaultInspector.tsx` 的 `buildMeasureFormat` 与
`MeasureFormatField`;新测试
`DatasetDefaultInspector.currencyNoGuess-27156.test.tsx`。
- **两分钟验证:** Studio 打开一个数据集,给 sum(金额) 度量选「货币」不选币种并保存,度量定义里没有 `currency`
键,币种框显示「继承 —— 字段或组织的默认币种」。
- **需要你做的:** 无,席位落地。
## What changes
- `buildMeasureFormat('currency', …)` with an empty currency returns
`currency: undefined` instead of `'USD'`, and the currency picker no
longer pre-selects USD (it read `measure.currency || 'USD'`).
- The save path drops the key: a new module-private `writeMeasureFormat`
rebuilds the measure without `format` / `currency` when the picker
leaves them unset (the `writeRowField` rule), so the held draft and the
saved body both carry no `currency` key, rather than `currency:
undefined`.
- Over a `currency`-type source field the picker gains an "Inherit —
field or organization default" row (`''`), which is the default. Over
any other field it offers no inherit row and shows the placeholder
"Choose a currency".
- `parseMeasureFormat` takes a `monetaryField` flag: a code-less,
non-percent numeral format over a `currency` field reads back as
Currency · inherit.
- The kind the author picks is held in component state while the stored
format is still the one that pick wrote, so "Currency" does not snap
back to "Number" before a code is chosen over a plain number field.
- `@object-ui/i18n` studio namespace:
`engine.inspector.dataset.currencyInherit` and
`engine.inspector.dataset.currencyChoose` (en, zh). Patch changeset for
`@object-ui/i18n`.
## Why
Maintainer ruling 6464202770 on #27119 (Q2 = X): no surface guesses a
currency. The analytics chain resolves a monetary measure's currency as
explicit measure `currency`, then the source field's fixed currency,
then the tenant default; a stored guess outranks both.
## Tests
New `DatasetDefaultInspector.currencyNoGuess-27156.test.tsx` (6 cases,
the inspector mounted with a stateful draft holder):
- empty pick over a `currency` field: the measure has no `currency`
property (`not.toHaveProperty`, so `currency: undefined` would fail
too), `format: '0,0'`, the picker reads inherit;
- CONTROL: picking CNY stores `currency: 'CNY'`;
- a loaded `currency: 'USD'` measure: no patch on mount, the picker
shows USD, a decimals change keeps `currency: 'USD'`;
- over a number field: no `currency` key, kind stays Currency, the
picker shows "Choose a currency" and offers no inherit row;
- load: a code-less format over a `currency` field reads Currency ·
inherit; CONTROL: the same format over a number field reads Number.
Ablation (committed fix, `node scripts/ablation-replace.mjs` with anchor
`currency: currency || undefined };` replaced by `currency: currency ||
'USD' };`): 2 failed / 4 passed — "stores no currency key when Currency
is picked with no code" and the number-field case go red; restore
verified (blob `10ecdd83` equals HEAD, `git diff HEAD` empty).
## Acceptance notes
- **Stored currencies are left as authored.** A measure that already
carries `currency: 'USD'` (including one this inspector wrote earlier)
is explicit on the wire: no migration, no rewrite on load, and it
round-trips through edits unchanged (pinned above).
- **Mechanism assumption measured, partly disproved.** The order assumed
the empty state reads as "inherit" for every measure. The analytics
service treats a measure as monetary only when it declares a currency or
aggregates a `currency`-type field; any other measure never receives a
code. So "inherit" is offered only over a `currency` field; over other
fields the empty state asks for a code, following the ruling's intent
(no guess) rather than offering an inherit that resolves to nothing.
- **Read-back change over `currency` fields.** A code-less numeral
format over a `currency` field used to read back as "Number"; it now
reads as Currency · inherit. The runtime already rendered those measures
with a currency, so the new reading matches what dashboards show. A
consequence: choosing "Number" over a `currency` field reads back as
Currency after a reload, because the runtime attaches a currency to it
either way.
- **The sample under the picker** shows a plain number for an inherit
measure: the inspector does not know the field's fixed currency or the
tenant default. Noted, not changed.
- **USD scan of the write path:** after this diff the only `'USD'` left
in `views/metadata-admin` (non-test) is the `CURRENCY_OPTIONS` row
itself. The `formatKind.currency` label still shows `$1,234.50` as an
example; it is display text and writes nothing.
- Serial: open PR #27163 edits this file's imports and filter section,
not `buildMeasureFormat`; `origin/main` was merged before the gates.
## Verification
Final head `77475026`. The last commit only drops an `export` keyword;
the derived gates, the ui scripts suite and the two inspector test files
were re-run on it, the wider suites ran on `dcfac927`.
<details><summary>Local runs</summary>
- On `77475026`: `node scripts/pm/dispatch-gates.mjs --commands` (list
unchanged), all 34 families exit 0, `--ran` reconciliation 34 derived,
34 run, 0 NOT-MEASURED; `pnpm --dir ui exec vitest run scripts/` plus
`DatasetDefaultInspector.currencyNoGuess-27156.test.tsx` and
`DatasetDefaultInspector.test.tsx` (verify lock): 177 files passed, 2
skipped; 5040 tests passed, 2 skipped.
- On `dcfac927`: `pnpm --dir ui exec vitest run
packages/app-shell/src/views/metadata-admin/inspectors/ packages/i18n/`
(verify lock): 286 files passed, 4116 tests passed, 14 skipped.
- `pnpm --filter @object-ui/app-shell --filter @object-ui/i18n run
type-check`: both `type-check: Done`, exit 0.
- `pnpm --dir ui run check:new-line-citations`: 0 new citations, exit 0.
- `eslint --no-inline-config` on the four changed files: 0 errors
(pre-existing warnings only).
- `dispatch-gates --tier`: no path-derived mandate; 233 changed lines.
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(not touched)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes. (changed files linted
locally; repo-wide run is CI's)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (not touched)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English. (the 维护者速读 block is
the dispatch order's Chinese summary)
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbakr3SQTyXX1NuZkt3)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
fix(studio): the dataset inspector stores no guessed measure currency
6
+
7
+
Clause-②: no
8
+
Metadata-protocol: none
9
+
10
+
Picking the "Currency" display format for a dataset measure with no currency chosen used to store `currency: 'USD'`, and the currency picker pre-selected USD. A measure's own `currency` outranks the source field's fixed currency and the tenant default, so a measure saved that way showed `$` on a tenant whose default is another currency. The inspector now guesses nothing:
11
+
12
+
-**Over a `currency` field** the picker opens on "Inherit — field or organization default" and the saved measure carries no `currency` key, so it resolves the field's fixed currency, then the tenant default. A numeral format with no code over such a field reads back as Currency · inherit.
13
+
-**Over any other field** there is nothing to inherit, so the picker offers no inherit row and asks for a code; the format kind stays Currency until one is picked.
14
+
-**A stored `currency`**, `'USD'` included, is explicit authoring and is left as written: no migration, no rewrite on load.
15
+
16
+
`@object-ui/i18n` gains `engine.inspector.dataset.currencyInherit` and `engine.inspector.dataset.currencyChoose` in the studio namespace (en, zh).
0 commit comments