Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/28051-skills-derived-currency-refusal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@objectstack/skills': patch
---

docs(skills): the skills teach the third `fixed`-currency refusal, a derived measure's own `currency` against its operands' one `fixed` code

Clause-②: no
Metadata-protocol: none

- `objectstack-data` `rules/field-types.md` (Currency resolution) and `objectstack-ui` `rules/dashboards.md` (the dataset currency comment) taught two `fixed` refusals and missed the third: a derived `sum` or `difference` whose monetary operands all resolve to one `fixed` code refuses another `currency` at dataset compile, with `DATASET_INVALID` / 400. Both files now teach it beside the other two, with the shipped prescription: delete the measure's `currency` (the column takes the operands' code), or restate that code.
- Paid for inside each file: `field-types.md` drops the `currency_code` naming convention its own example already shows; `dashboards.md` drops the retired `categoryGranularity` parenthetical, which the parse refusal and the Dataset-Bound Widgets paragraph already carry. Neither ratchet row moves.
- No schema, key or behaviour changes.
12 changes: 9 additions & 3 deletions content/docs/api/data-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -518,12 +518,18 @@ monetary measure column takes the measure's own `currency`; then, only when the
field is `currencyMode: 'fixed'`, that field's `currencyConfig.defaultCurrency`; then the
tenant default currency (the `localization.currency` setting), if one is set. A
`dynamic` field (the default mode) never lends its `defaultCurrency` — only `fixed` does —
so its column shows the tenant default. The cube query on this page carries no column
`currency`.
so its column shows the tenant default. A derived `sum` or `difference` whose monetary
operands all resolve to one `fixed` code takes that code at the field's step; any other
derived measure (a `ratio`, a `product`, operands in the tenant's currency) is monetary
only by its own `currency`, else its column carries none. The cube query on this page
carries no column `currency`.

Over a `fixed` field the measure's `currency` may only restate the field's code. A
different code is refused when the dataset compiles, with `DATASET_INVALID` / 400: delete
the measure's `currency`, and the column takes the field's code. A derived measure whose
the measure's `currency`, and the column takes the field's code. A derived `sum` or
`difference` whose operands resolve to one `fixed` code is held to the same rule: a
different `currency` is refused with `DATASET_INVALID` / 400 — delete the measure's
`currency` (the column takes the operands' code), or restate it. A derived measure whose
monetary operands resolve to two different `fixed` codes is refused the same way, whatever
the operation: derive it from measures in one currency, or drop the operand in the other.
Neither a `fixed` code against the tenant default nor a measure `currency` over a
Expand Down
8 changes: 6 additions & 2 deletions content/docs/data-modeling/currency.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,12 @@ that column. A `count` column never carries `currencyField`. See the
- **A derived measure whose monetary operands resolve to two different `fixed` codes** is
refused at compile with `DATASET_INVALID` / 400, whatever the operation. Derive it from
measures in one currency, or drop the operand in the other. A derived `sum` or
`difference` over one `fixed` code carries that code, so a measure built on it is judged
the same way.
`difference` over one `fixed` code carries that code, on its result column too, so a
measure built on it is judged the same way.
- **A derived `sum` or `difference` whose `currency` differs from its operands' one `fixed`
code** is refused at compile with `DATASET_INVALID` / 400: `currency: 'USD'` over two
measures fixed to `EUR` would print euros as dollars. Delete the measure's `currency`, and
the column takes the operands' code, or restate that code.
- **Not judged:** a `fixed` code against the tenant currency, which compile does not know,
and a measure `currency` over a `dynamic` field, which wins as the top of the currency
chain.
Expand Down
7 changes: 4 additions & 3 deletions skills/objectstack-data/rules/field-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,10 @@ Stored as JSON on the parent row — no separate table / FK:
`{ value, currency }`. Shown: `'fixed'`, `defaultCurrency`; `'dynamic'`
(default), the tenant `localization.currency`, else a plain number (never
`$`); `'record'`, each record's code in the `text` field `currencyField`
names (by convention `currency_code`), with `valueDomain: 'iso_4217_currency'`. A
dataset measure's own `currency` wins over a `'dynamic'` field; over a `'fixed'` one
it may only restate the code, else `DATASET_INVALID` (objectstack-ui →
names, with `valueDomain: 'iso_4217_currency'`. A
dataset measure's own `currency` wins over a `'dynamic'` field; over a `'fixed'` one,
or a derived sum/difference over one `'fixed'` code, it may only restate that code,
else `DATASET_INVALID`: delete it, and the column takes the code (objectstack-ui →
Dashboards). A `'fixed'` code is not judged against the tenant's. Switched a
stored field to `'record'`? Backfill: `os migrate record-currency-codes`.

Expand Down
8 changes: 4 additions & 4 deletions skills/objectstack-ui/rules/dashboards.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,8 +282,7 @@ The **dataset dimension carries the default; a widget's
`options.dateGranularity` overrides it for that widget only.** Give a date
dimension a `dateGranularity` and any presentation that selects it groups by that
bucket server-side — without it every distinct timestamp becomes its own
category, collapsing a 12-row seed into a 12-point flat line. (The old widget-level
`categoryGranularity` was removed in the single-form cutover.)
category, collapsing a 12-row seed into a 12-point flat line.

<!-- os:check -->
```typescript
Expand All @@ -301,8 +300,9 @@ defineDataset({
// `currencyMode: 'record'` amount refuses `currency`, and sum/avg/min/max
// unless grouped by its code field; then the column carries
// `currencyField` (the per-row code column); a `count` never does. A `fixed`
// field refuses a different `currency` (delete it; restating is fine), and
// a derived measure refuses operands in two `fixed` codes (derive from one
// field, or a derived sum/difference over one `fixed` code, refuses another
// `currency` (delete it, the column takes that code; restating is fine); a
// derived measure refuses operands in two `fixed` codes (derive from one
// currency or drop one). Not judged: `currency` over a `dynamic` field, a
// `fixed` code against the tenant's.

Expand Down
Loading