Skip to content

Commit 5268f50

Browse files
docs(spec): name a currency amount's decimal places as its CLDR fraction digits, not its ISO 4217 minor unit (#30755)
<!-- claude-projects-attribution: {"github_login":"os-sales"} --> _Requested by **Sales** · [project thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuPwsJioKJXJfMZ5KaTMSQpj)_ domain:spec#2 · os-sales · session_01L6bhgWFZUMBKFD9Lcwt4DU Fixes #30648 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么:** 把“币种金额的小数位 = ISO 4217 最小单位”这句话全部改成“= 币种的 CLDR 小数位”:spec 的两条 `scale` 拒收文案、`currencyConfig` 小数位提示、`precision`/`scale` 字段说明、升级指南条目、四个文档页,以及 ui 侧逐字镜像的拒收串、控制台警告和注释。 - **为什么改:** 平台实际用的是 CLDR(`CURRENCY_FRACTION_DIGITS` 和各显示面的 `Intl`),有 16 个币种与 ISO 4217 不同(如 IQD:CLDR 0 位,ISO 3 位)。旧文案让伊拉克第纳尔、匈牙利福林等币种的作者以为会显示小数,平台却按整数显示。 - **风险与回滚:** 只改文字,不改任何表值、拒收分支、规则 id 或路径;接受与拒收的集合不变。唯一可见变化是拒收消息的措辞,按旧原文匹配消息的检查会失配。回滚即撤销本 PR。 - **先看:** ① `packages/spec/src/data/field.zod.ts` 的 currency `scale` 拒收(L3161)与 `INLINE_GRID_CURRENCY_SCALE_REFUSAL`(L1084);② `ui/packages/types/src/zod/form.zod.ts` 的 `CURRENCY_FIELD_SCALE_REFUSAL`(L1089,与 spec 逐字相等);③ `packages/spec/src/data/field-currency-scale-refused.test.ts` 末尾新增的 describe(L258)。 - **两分钟验证:** `pnpm --filter @objectstack/spec exec vitest run src/data/field-currency-scale-refused.test.ts`,预期 13 条全过,其中新 pin 断言拒收消息含 “CLDR fraction digits”、不含 “ISO 4217 minor unit”。 - **需要你做的:** 无,席位落地。 ## What changes **The refusal sentence, FROM → TO** (spec `FieldSchema` currency `scale` refusal, held word for word by ui `CURRENCY_FIELD_SCALE_REFUSAL`): - FROM: "…not a field setting: the currency's ISO 4217 minor unit (2 for USD, 0 for JPY, 3 for KWD) decides how the amount displays, and the field's write allowance stays unconstrained…" - TO: "…not a field setting: the currency's CLDR fraction digits (2 for USD, 0 for JPY, 3 for KWD) decide how the amount displays, and the field's write allowance stays unconstrained…" The inline grid column refusal (`INLINE_GRID_CURRENCY_SCALE_REFUSAL`, and its `console.warn` copy in `ui/packages/plugin-form/src/deriveMasterDetail.ts`) moves the same way: "…the currency's CLDR fraction digits (2 for USD, 0 for JPY, 3 for KWD) decide how the cell displays the amount and the width a computed amount is rounded to." The first sentence and the remedy ("delete the key") are unchanged everywhere. The examples stay: USD 2, JPY 0 and KWD 3 are the same in both systems. The wording follows PR #30644 (`currency-fraction-digits.ts`, `currency.mdx`): "CLDR fraction digits, not ISO 4217's minor units". Where a docs page explains the difference, it gives IQD (0 in CLDR, 3 in ISO 4217) as the example of the sixteen codes and links the currency page that lists them. - **Spec runtime strings and describes** (`field.zod.ts`): the `currencyConfig` decimal-places reason `CURRENCY_DECIMAL_PLACES_ARE_THE_CURRENCYS` (it feeds the `precision` tombstone and the `decimals` / `scale` prescriptions), the inline grid refusal, the field refusal, and the `precision` / `scale` / inline column `scale` describes. Comments in the same file at L504, L711, L3139, L3145 and L3171. - **ADR-0087 entries** (words only, no new entry): the conversion `currency-config-precision-removed`, the semantic entries `currency-config-precision-retired`, `field-currency-scale-refused`, `inline-grid-column-currency-scale-refused`, `inline-grid-column-identity-only-currency-scale-refused`, `ui-object-master-detail-form-details-closed`, `ui-page-panel-children-cross-references-judged`, `ui-record-line-items-props-closed`, and `registry.ts.template`. The generated upgrade guide (in memory and `--out`) now carries "CLDR fraction digits" 10 times; its only "ISO 4217 minor unit" left are the three quoted rulings. - **Spec comments:** `field-scale.ts`, `field.form.ts`, `retired-keys/18.data__CurrencyConfig__precision.ts`, `liveness/field.json` (the `currencyConfig` row's note). - **Docs:** `data-modeling/field-types.mdx`, `data-modeling/validation-rules.mdx`, `protocol/objectql/schema.mdx`, `protocol/objectql/types.mdx`. `content/docs/references/**` (field, object, migration, component) regenerated with `gen:docs`, not hand-edited. - **objectql:** the one comment in `validation/record-validator.ts`. - **ui:** `CURRENCY_FIELD_SCALE_REFUSAL`, the `deriveMasterDetail` warning, and the docblocks and comments in `types/src/form.ts`, `fields/src/{currency.ts,index.tsx}`, `fields/src/widgets/{CurrencyField.tsx,GridField.tsx,PercentField.tsx,number-format.ts}`, `plugin-dashboard/src/ObjectMetricWidget.tsx`, `plugin-form/src/{MasterDetailForm.tsx,deriveMasterDetail.ts}`, `plugin-grid/src/useColumnSummary.ts`, `app-shell/.../ObjectFieldInspector.tsx`, plus ui test docblocks and two test titles that repeated the sentence. - **Changesets, all `patch`:** `@objectstack/spec`, `@object-ui/types`, `@object-ui/fields`, `@object-ui/plugin-form`, one per package whose published bytes change (measured below). No changeset for `@objectstack/objectql`, `@object-ui/plugin-grid` and `@object-ui/plugin-dashboard` (comments only, absent from their built `dist/`) or `@object-ui/app-shell` (private). The branch is the harness-assigned `claude/project-thread-437a6c`, not `claude/issue-30648-*`. ## Hit census and dispositions Base `4bf9a2d7`. The card's one-line `git grep -n -i "ISO 4217 minor unit"` misses line-wrapped spellings, so the census is a multi-line search over every tracked file mentioning `4217` (CHANGELOGs, `content/docs/releases/**` and translation packs excluded): `ISO`, then `4217`, then one of minor / decimal / fraction / width / digit, with whitespace, comment stars and string-concatenation characters allowed between them. 116 lines at base (13 of them in the generated `content/docs/references/**`), plus `ui/packages/fields/src/currency.ts` L41 ("fraction digits ISO 4217 gives") and the test title `formatCurrency.minorUnits.test.tsx` L85 ("as ISO 4217 says"), found by a second search for "ISO 4217 gives / says". <details><summary>Every hit, with its disposition</summary> **Reworded** (the file surface of claim 6671521736, widened by amendment 6671693654): - `packages/spec/src/data/field.zod.ts` L492, L504-505, L711-712, L1086-1087, L1159, L1463, L1488, L3139, L3145, L3162, L3171 - `packages/spec/src/data/field-scale.ts` L107, L109 · `field.form.ts` L81 · `conversions/entries/18/currency-config-precision-removed.ts` L22, L53 · `migrations/entries/retired-keys/18.data__CurrencyConfig__precision.ts` L9 · `migrations/registry.ts.template` L1630 · `liveness/field.json` L432 - Semantic entries: `18.currency-config-precision-retired` L19, L29-30 · `18.field-currency-scale-refused` L12, L21, L43 · `18.inline-grid-column-currency-scale-refused` L14 · `18.inline-grid-column-identity-only-currency-scale-refused` L22 · `18.ui-object-master-detail-form-details-closed` L32 · `18.ui-page-panel-children-cross-references-judged` L25 · `18.ui-record-line-items-props-closed` L35 - Spec pins: `currency-precision-iso4217.test.ts` L11-12, L60, L108, L227 · `field-currency-scale-refused.test.ts` L8, L52, L141 · `inline-grid-column-currency-scale-refused.test.ts` L63 · `field-scale.test.ts` L58 - Docs: `field-types.mdx` L237, plus L250 and L598, which said a currency cell uses "the currency's minor unit" on the same page · `validation-rules.mdx` L160 · `protocol/objectql/schema.mdx` L293 · `protocol/objectql/types.mdx` L33, L343 · regenerated `references/data/field.mdx` (4), `references/data/object.mdx` (4), `references/system/migration.mdx` (4), `references/ui/component.mdx` (1) - `packages/objectql/src/validation/record-validator.ts` L1528 - ui: `types/src/zod/form.zod.ts` L1091 · `types/src/form.ts` L2074 · `plugin-form/src/deriveMasterDetail.ts` L255, L301 · `plugin-form/src/MasterDetailForm.tsx` L490 · `plugin-grid/src/useColumnSummary.ts` L417 · `plugin-dashboard/src/ObjectMetricWidget.tsx` L605 · `app-shell/.../ObjectFieldInspector.tsx` L562 · `fields/src/currency.ts` L25, L41 · `fields/src/index.tsx` L582-583, L915-916 · `fields/src/widgets/CurrencyField.tsx` L22-23 · `GridField.tsx` L127-128, L327 · `PercentField.tsx` L90 · `number-format.ts` L105-106 - ui tests (docblocks and two titles, no assertion): `ObjectFieldInspector.currencyScale-10221` L9 · `CurrencyField.minorUnits` L16 · `PercentCurrencyField.readonlyDisplay-11444` L26-27, L130 (title) · `formatCurrency.centsDigit` L29 · `formatCurrency.minorUnits` L10-11, L30-31, L85 (title) · `GridField.currencyMinorUnit-10355` L11 · `GridField.currencyScaleNotRead-10783` L10-11 **Quoted ruling, kept as written** (ruling 乙 「a currency's ISO 4217 minor unit decides its display」, quoted, not paraphrased): `field.zod.ts` L1077 · `inline-grid-column-currency-scale-refused.test.ts` L7 · `18.inline-grid-column-currency-scale-refused` L19 · `18.ui-object-master-detail-form-details-closed` L40 · `18.form-view-subform-columns-closed` L29. **Already correct, not touched** (they name CLDR and use ISO 4217 only as the contrast): `currency-fraction-digits.ts` (5) · `content/docs/data-modeling/currency.mdx` L303 · `packages/spec/src/data/index.ts` L199 · `packages/objectql/src/home-amount-projection.ts` L36 · ADR-0144 (4, a governed surface) · `.changeset/30103-home-amount-rate-field-scale-floor.md` (2). **Out of scope:** `ui/packages/i18n/src/__tests__/machineLocaleCensus-9909.test.ts` L255 (open PR #30552 touches the file; see Acceptance notes) · every `CHANGELOG.md`. </details> **After, at this PR's head:** the same search answers 33 lines, and none of them says ISO 4217 is where a currency's decimal places come from: - 5 quoted rulings (listed above); - 14 already-correct lines (listed above); - 1 in `machineLocaleCensus-9909.test.ts` L255 (kept out, see Acceptance notes); - 13 written by this PR that name ISO 4217 only as the contrast: 5 in its changesets, 5 in the new pin (`not.toContain('ISO 4217 minor unit')` and its comment and titles), and 3 docs lines ("CLDR fraction digits (not ISO 4217's minor units)": `field-types.mdx` L238, `validation-rules.mdx` L160, `types.mdx` L343). ## Tests - **Pins moved to the new words:** `currency-precision-iso4217.test.ts` (the `precision` tombstone and the `decimals` / `scale` prescriptions), `field-currency-scale-refused.test.ts` (the ruled-remedy helper and the `scale` describe), `inline-grid-column-currency-scale-refused.test.ts` (`MINOR_UNIT_CLAUSE` renamed `FRACTION_DIGITS_CLAUSE`), and the comment in `field-scale.test.ts`. - **New pin** (one top-level `describe` in the existing harness `field-currency-scale-refused.test.ts`): the `FieldSchema` refusal for `scale` on a `currency` field is `code: 'custom'`, contains "CLDR fraction digits" and does not contain "ISO 4217 minor unit"; the `scale` describe says the same. - **Parity pin unchanged and green:** `ui/packages/types/src/__tests__/strict-face-read-keys-11070.test.ts` compares `CURRENCY_FIELD_SCALE_REFUSAL` with the spec's issue message for the same entry. ## Acceptance notes - `ui/packages/i18n/src/__tests__/machineLocaleCensus-9909.test.ts` L255 still says "an ICU metadata PROBE for the ISO-4217 fraction-digit count". It is a census reason string in a test, not shown to authors. Open PR #30552 touches the same file, so the seat kept it out of this PR (amendment 6671693654). It can be reworded after #30552 lands. - Lines that name the retired #7918 check as an "ISO 4217" check ("declared and validated against ISO 4217", "its own #7918 ISO 4217 contradiction check") are left as written: they name what that check was called, not where a currency's decimal places come from (`registry.ts.template` L1629, `currency-config-precision-removed.ts` L23 and L54, `field.zod.ts` L510, `currency-precision-iso4217.test.ts` L9, `retired-keys/18.data__CurrencyConfig__precision.ts` L5-6). - The bare term "minor unit" with no "ISO 4217" (for example "the currency's minor unit decides" in ui test titles and comments) is left as written, except the two sentences on `field-types.mdx`, the author-facing page this PR already rewords. It is the generic name for a currency's sub-unit, and CLDR's digits are that count too. - The sixteen codes were not re-derived here. The docs pages that name an example use IQD, from PR #30644's table. ## Verification <details><summary>Gates (local subset, head b15d64c)</summary> Exit codes captured before any pipe. - Review round 1 (seat contract review 6673665638, S1): `b15d64c6` changes one comment only, the docblock above `INLINE_GRID_CURRENCY_SCALE_REFUSAL` in `packages/spec/src/data/field.zod.ts` (L1081), from "the same minor-unit clause" to "the same fraction-digits clause", the name the pin now uses (`FRACTION_DIGITS_CLAUSE`). At `b15d64c6`: `pnpm --filter @objectstack/spec build` 0; spec `vitest run` (all projects, a wider run than the package `test` script's `--project local` 817 files) 907/907 files, 27366 passed, 1 todo; spec `tsc --noEmit` 0; eslint `--no-inline-config` on `field.zod.ts` 0; `rg -n -U -i "minor[\s*-]*unit[\s*]*clause"` over `field.zod.ts` and the inline-grid pin finds nothing; the multi-line census still reads 33 lines, the same set as at `fbb6e7fb`. `dispatch-gates --commands` derives the same 112 families as at `fbb6e7fb`, all 112 exit 0; `--ran`: "112 derived famil(ies) accounted for — 112 run, 0 NOT-MEASURED (a DERIVED zero …)". No ablation re-run (comment only). The figures below are from `fbb6e7fb`. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at `fbb6e7fb`: 112 derived families, all 112 exit 0. `--ran` (with `:: exit N` per line): "112 derived, 112 run, 0 NOT-MEASURED, 0 UNRUN … a DERIVED zero — all 112 recorded an exit code and none of them is 3". - The same 112 ran first at `82aa23e3` (before the main merge): 108 exit 0 and 4 exit 3 PREREQUISITE NOT MET on a fresh clone (`check-plugin-teardown-shape --self-test` needed its pinned fixture commit fetched; spec `check:skill-examples`, `check:dual-build-cjs-loads` and `check:i18n` needed unbuilt dists). After `git fetch --depth=1` of the fixture and one turbo build of the 39 missing packages, all four re-ran exit 0; the run at `fbb6e7fb` needed no re-run. - `pnpm --filter @objectstack/spec build` 0; `check:generated` 0 ("All 14 generated artifacts are up to date"), after `gen:docs` regenerated the four reference pages; `check:spec-changes` 0, `check:upgrade-guide` 0, `node scripts/check-adr-0087-registration.mjs --base origin/main` 0 ("adds no declared-breaking changeset"). - `@objectstack/spec` test: 817/817 files, 25827 passed, 1 todo (lock VERDICT command-exit 0, after the main merge; 25793 before it). typecheck 0. - `@object-ui/types` test 387/387 files (10464 tests); `@object-ui/fields` 298 passed, 1 skipped (4467 tests); `@object-ui/plugin-form` 223/223 (2454 tests); `@object-ui/plugin-grid` 278/278 (2392); `@object-ui/plugin-dashboard` 203/203 (1957); `@object-ui/app-shell` `ObjectFieldInspector.currencyScale-10221.test.tsx` 5/5. After the main merge: the parity file 94/94 and the app-shell file 5/5 again. The first ui run failed to resolve `@objectstack/formula` / `@objectstack/client` (unbuilt dists on a fresh clone, no test ran); the recorded numbers are the run after building them. - `type-check` 0 for `@object-ui/types`, `fields`, `plugin-form`, `plugin-grid`, `plugin-dashboard` (after building the ui dependency closure; before it, TS2307 on unbuilt `@object-ui/*` dists). `@object-ui/app-shell` type-check not run: its two files change comments only. - eslint `--no-inline-config` on the 37 changed `.ts` / `.tsx` files at `fbb6e7fb`: root config 0 errors, `ui/` config 0 errors (279 warnings, all `no-explicit-any` on lines this PR does not touch). - Full dogfood battery: not owed (no `!` in the title, no `(narrowing)` arm). - PR check-runs at `fbb6e7fb`: every check suite completed with none failed (check_suite.completed, 2026-11-06T01:10Z). At `b15d64c6`: not read at the time of writing. </details> <details><summary>Ablation: the old refusal clause back in the spec only</summary> Run with `node scripts/ablation-replace.mjs` (run mode: mutate, run the leg, restore with proof) under `os-verify-lock.sh`, on committed head `82aa23e3`. The anchor is the TO clause of the field refusal in `packages/spec/src/data/field.zod.ts`; the replacement is the FROM clause. Nothing under `ui/` was changed. ``` ablation-replace: anchor "the currency\\'s CLDR fraction digits (2 for ' +" x1 (before) ablation-replace: anchor x1 -> x0 ablation-replace: replace "the currency\\'s ISO 4217 minor unit (2 for ' +" x0 -> x1 ablation-replace: ok mutation landed: anchor 1 -> 0, blob e7f40f2 -> 1cf29ccb760c spec build EXIT=0 ablation-dist-preflight: @objectstack/spec -- expecting "ISO 4217 minor unit (2 for " in packages/spec/dist ✓ dist/: marker present in 2 built files -- the ablation is live in the artifact the suite consumes. spec field-currency-scale-refused.test.ts: Tests 4 failed | 9 passed (13) × the refusal and the `scale` describe say CLDR and no longer say "ISO 4217 minor unit" (the new pin) × refuses the designer-produced shape … with the ruled remedy (and two more ruled-remedy cases) ui strict-face-read-keys-11070.test.ts: Tests 1 failed | 93 passed (94) × `scale` on a `currency` entry is refused AT `scale` on both faces, with the spec's own refusal (round 13) AssertionError: expected '`scale` is not valid on a `currency` …' to be '`scale` is not valid on a `currency` …' // Object.is equality ablation-replace: ok restored: blob == HEAD (e7f40f2) and `git diff HEAD` is empty ``` Restore leg: `git diff HEAD` on the file 0 bytes, blob `e7f40f20…` equals `HEAD:packages/spec/src/data/field.zod.ts`; spec rebuilt; `ablation-dist-preflight.mjs … --absent` exit 0 ("marker absent from all 592 built files", "working tree clean against HEAD"); the four spec pin files 59/59 and the ui parity file 94/94 green again; `git status --porcelain` empty. </details> <details><summary>Published bytes: which packages need a changeset</summary> Each package built at this head, then `grep` over its `dist/` for the new words, with a control. | Package | New words in `dist/` | Control | Changeset | |:---|:---|:---|:---| | `@objectstack/spec` | refusal strings in `dist/*.js`; `src/**/*.zod.ts` ships too | — | `patch` | | `@object-ui/types` | `dist/zod/form.zod.js`, `dist/zod/index.zod.js`, `dist/form.d.ts` | — | `patch` | | `@object-ui/plugin-form` | `dist/index.js`, `dist/index.umd.cjs` (the warning) | — | `patch` | | `@object-ui/fields` | `dist/currency.d.ts` (the exported `currencyFractionDigits` docblock) | — | `patch` | | `@object-ui/plugin-grid` | none (comment inside a function) | neighbouring comment words absent, runtime `formatCurrency` present | none | | `@object-ui/plugin-dashboard` | none (comment inside a function) | same control | none | | `@objectstack/objectql` | none (comment inside a function) | neighbouring comment words absent, runtime `max_scale` present in `dist/core.js` | none | | `@object-ui/app-shell` | not measured | `private: true`, publishes nothing | none | The docs pages and `content/docs/references/**` are not a published package. </details> --- _Generated by [Claude Code](https://claude.ai/code/session_01L6bhgWFZUMBKFD9Lcwt4DU)_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01L6bhgWFZUMBKFD9Lcwt4DU <!-- signed: domain:spec#2 · os-sales · session_01L6bhgVugtCCwBs98FeR27P --> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2445cb4 commit 5268f50

51 files changed

Lines changed: 183 additions & 115 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
docs(spec): a currency amount's decimal places are named as its currency's CLDR fraction digits, not its ISO 4217 minor unit
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The currency `scale` refusal, the inline grid column's currency `scale` refusal, the `currencyConfig` decimal-places prescription, the `precision` and `scale` describes and the ADR-0087 upgrade-guide entries said "the currency's ISO 4217 minor unit". The platform's digit source is CLDR's fraction digits: `CURRENCY_FRACTION_DIGITS` is a CLDR `currencyData` snapshot, and every display face asks `Intl`, which reads the same data. For sixteen codes the two differ and CLDR's count applies: IQD has 0 (ISO 4217: 3), and AFN, ALL, COP, HUF, IDR, IRR, KPW, LAK, LBP, MGA, MMK, PKR, SOS, SYP and YER have 0 (ISO 4217: 2).
11+
12+
Words only. Nothing is newly accepted or refused, and the remedy is unchanged: delete `scale` on a `currency` field or column. The refusal now reads "the currency's CLDR fraction digits (2 for USD, 0 for JPY, 3 for KWD) decide how the amount displays". A check that matches the old text "ISO 4217 minor unit" in the message no longer matches.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/fields': patch
3+
---
4+
5+
docs(fields): `currencyFractionDigits` is documented as CLDR's fraction digits, not ISO 4217's
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The published declaration of `currencyFractionDigits` said it returns "the number of fraction digits ISO 4217 gives" a currency. It asks `Intl`, which reads CLDR's `currencyData`, so it now says CLDR. For sixteen codes the two differ (IQD 0 where ISO 4217 says 3, and fifteen codes 0 where ISO 4217 says 2), and the function has always answered CLDR's count. Words only: no return value changes.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/plugin-form': patch
3+
---
4+
5+
fix(plugin-form): the warning for a `scale` on a currency inline grid column names the currency's CLDR fraction digits
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The console warning for an inline grid column that renders as `currency` and carries `scale` repeats the spec's refusal, which now says "the currency's CLDR fraction digits (2 for USD, 0 for JPY, 3 for KWD) decide how the cell displays the amount" instead of naming the ISO 4217 minor unit. Words only: the same columns are reported, and the column still renders at its currency's digits.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/types': patch
3+
---
4+
5+
fix(types): the form face's currency `scale` refusal names the currency's CLDR fraction digits, the spec's wording
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`CURRENCY_FIELD_SCALE_REFUSAL` is held equal to `@objectstack/spec`'s currency `scale` refusal, which now says "the currency's CLDR fraction digits (2 for USD, 0 for JPY, 3 for KWD) decide how the amount displays" instead of naming the ISO 4217 minor unit. Words only: the same entries are refused at the same path.

‎content/docs/data-modeling/field-types.mdx‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,11 @@ what a record-currency amount refuses.
234234
{ name: 'price', label: 'Price', type: 'currency', currencyConfig: { currencyMode: 'fixed', defaultCurrency: 'USD' } }
235235
```
236236

237-
A currency amount's decimal places are its currency's ISO 4217 minor unit
238-
(2 for USD, 0 for JPY, 3 for KWD), not a field setting, and they are declared
239-
nowhere. A `currency` field takes no `scale`, and `currencyConfig` has no
237+
A currency amount's decimal places are its currency's CLDR fraction digits
238+
(2 for USD, 0 for JPY, 3 for KWD; not ISO 4217's minor units, which differ for
239+
sixteen codes such as IQD, 0 in CLDR and 3 in ISO 4217: see
240+
[Currency fields](/docs/data-modeling/currency)), not a field setting, and they
241+
are declared nowhere. A `currency` field takes no `scale`, and `currencyConfig` has no
240242
decimal-places key: publish-time validation refuses `scale` on the field and
241243
`currencyConfig.precision` (removed in `@objectstack/spec` 17.5.0 — no renderer
242244
or runtime ever read it), each with a prescription to delete the key. The
@@ -245,7 +247,7 @@ amount declares `precision: 18` — and is never compared with the currency.
245247

246248
**In the Console:** a numeric input with the currency's symbol. The read-only display, the
247249
grid cell, the input's `step` and the rounding applied when the input loses focus all use the
248-
currency's minor unit, so a JPY amount shows no decimals and a KWD amount shows three. With no
250+
currency's CLDR fraction digits, so a JPY amount shows no decimals and a KWD amount shows three. With no
249251
currency resolved at all, an amount shows two decimals. A `record`-mode amount resolves only
250252
its own record's code, never the tenant default. A field-level `currency` key is
251253
refused: the currency is `currencyConfig`. The input is a native number box; see
@@ -593,7 +595,7 @@ the parent's form edits the children as rows of a table:
593595
- **Booleans.** A derived `boolean` or `toggle` child field becomes a `select` cell storing
594596
`'true'` / `'false'`, labelled in the session language.
595597
- **Currency.** A `currency` cell shows the resolved currency's symbol, and decimals of that
596-
currency's minor unit; `prefix` replaces the symbol only.
598+
currency's CLDR fraction digits; `prefix` replaces the symbol only.
597599
- **Order.** When the columns are derived and the child object has a field named `position`, `sort_order`, `sequence`,
598600
`line_no`, `line_number` or `sort`, the grid stamps each row's index into it whenever a row is
599601
added, removed, duplicated or dragged, so the order is saved with the rows.

‎content/docs/data-modeling/validation-rules.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ These properties apply to **all** field types and are validated by the base `Fie
157157
| `currencyConfig.currencyField` | `string` | — | Required under `record` and refused under the other two modes: a `text` field of the same object declaring `valueDomain: 'iso_4217_currency'` (the object refuses a pointer at anything else). A write that leaves a record-mode amount holding a value beside a blank code is refused, `ERR_CURRENCY_CODE_REQUIRED` / 400 |
158158
| `currencyConfig.homeAmount` | `object` | — | Optional under `record` and refused under the other two modes: the stored home-currency projection, three pointers into the same object, all required — `field` (a `dynamic`-mode `currency` field, `readonly: true`), `rateDate` (a `date` or `datetime` field, the document date that picks the rate) and `rateField` (a `number` field, `readonly: true`). See [Declaring the home amount](/docs/data-modeling/currency#declaring-the-home-amount) |
159159

160-
**Default constraints:** Stored as a **bare number** (a finite numeric scalar — `valueSchemaFor` routes `currency` to `z.number().finite()`); there is no `{ value, currency }` envelope on the value path. The amount never carries its currency code: under `dynamic` and `fixed` the code is field configuration, carried by `currencyConfig` above, and under `record` it is the value of the record's own code field, an ordinary `text` column judged by its `valueDomain` ([Currency fields](/docs/data-modeling/currency)). Decimal places are the currency's ISO 4217 minor unit (2 for USD, 0 for JPY, 3 for KWD) and are declared nowhere — `currencyConfig` has no decimal-places key (`currencyConfig.precision` is refused, with a prescription to delete it), and the field-level `precision` is the total digit count, never compared with the currency.
160+
**Default constraints:** Stored as a **bare number** (a finite numeric scalar — `valueSchemaFor` routes `currency` to `z.number().finite()`); there is no `{ value, currency }` envelope on the value path. The amount never carries its currency code: under `dynamic` and `fixed` the code is field configuration, carried by `currencyConfig` above, and under `record` it is the value of the record's own code field, an ordinary `text` column judged by its `valueDomain` ([Currency fields](/docs/data-modeling/currency)). Decimal places are the currency's CLDR fraction digits (2 for USD, 0 for JPY, 3 for KWD; not ISO 4217's minor units, which differ for sixteen codes such as IQD) and are declared nowhere — `currencyConfig` has no decimal-places key (`currencyConfig.precision` is refused, with a prescription to delete it), and the field-level `precision` is the total digit count, never compared with the currency.
161161

162162
### `percent`
163163

‎content/docs/protocol/objectql/schema.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ fields:
290290
| `valueDomain` | `string` | `text` | Standard the written value must belong to: `iana_time_zone`, `iso_4217_currency` or `iso_3166_alpha2` (the closed vocabulary shared with settings specifiers). A `record`-mode `currency` amount's code field is a `text` field declaring `iso_4217_currency` ([Currency fields](/docs/data-modeling/currency)). |
291291
| `min` | `number` | `number`, `currency` | Minimum numeric value. |
292292
| `max` | `number` | `number`, `currency` | Maximum numeric value. |
293-
| `scale` | `number` | `number`, `percent`, `rating`, `slider` | Decimal places, enforced on writes. Refused on `currency` — delete it there: a currency amount's decimal places are its currency's, so its ISO 4217 minor unit decides the display. |
293+
| `scale` | `number` | `number`, `percent`, `rating`, `slider` | Decimal places, enforced on writes. Refused on `currency` — delete it there: a currency amount's decimal places are its currency's, so its CLDR fraction digits decide the display. |
294294
| `precision` | `number` | `number`, `currency` | Total digits (including scale). |
295295
| `currencyConfig` | `object` | `currency` | Where the amount's currency comes from: `currencyMode` `dynamic` (the tenant default, the default), `fixed` (with `defaultCurrency`) or `record` (with `currencyField`, the sibling code field, and optionally `homeAmount`, the stored home-currency projection: `field` a `dynamic`-mode `currency` field, `rateDate` a `date` or `datetime` field, `rateField` a `number` field — see [Declaring the home amount](/docs/data-modeling/currency#declaring-the-home-amount)). |
296296
| `options` | `array` | `select`, `multiselect` | List of valid values. |

‎content/docs/protocol/objectql/types.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ revenue:
3030
# - Store a bare number, never a currency code beside it: it shows in
3131
# the tenant default currency (plain if none is set), one fixed on the
3232
# field, or each record's code in a declared sibling field, at that
33-
# currency's ISO 4217 decimal places
33+
# currency's CLDR fraction digits
3434
# - UI shows currency symbol
3535
# - Validate numeric precision
3636
# - Format for display ($1,234.56)
@@ -340,7 +340,7 @@ been removed from the spec.
340340
record's code is judged by its field's `valueDomain: iso_4217_currency`, so
341341
only ISO 4217 codes pass there
342342
- No decimal-places setting: an amount's decimal places are its currency's
343-
ISO 4217 minor unit. The field-level `precision` is the amount's **total**
343+
CLDR fraction digits (not ISO 4217's minor units). The field-level `precision` is the amount's **total**
344344
digit count (a DECIMAL(18,2) amount declares `precision: 18`), refused on
345345
write when exceeded, with the amount's written decimals counted in it
346346

0 commit comments

Comments
 (0)