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
10 changes: 10 additions & 0 deletions .changeset/32392-skills-parent-currency-path-follow-ups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@objectstack/skills': patch
---

docs(skills): the dashboards rule says a parent-path record-currency amount admits no grouping, and the data evals measure the parent-path `currencyField`

Clause-②: no
Metadata-protocol: none

`objectstack-ui`'s dashboards rule said a `currencyMode: 'record'` amount refuses `sum` / `avg` / `min` / `max` "unless grouped by its code field". For a `master_detail` child amount whose `currencyField` is the parent path (`'invoice.currency_code'`), no grouping is admitted: the engine's aggregate door refuses it whatever it groups by, and the analytics cube door never matches a dimension to a dotted code field. A dashboard author who grouped by the path was refused. The rule now says so in one parenthesis, points to `objectstack-data` → Field Types → Currency for the declaration, and names the two single-currency shapes to measure instead: the amount's `homeAmount` field or the parent's `summary`. To pay for it, two clauses whose home is the package source are deleted from the same comment: what a result column carries (`AnalyticsResult.fields` in `@objectstack/spec/contracts`) and the "not judged" list of the measure-currency check. `objectstack-data`'s `objects-fields-relationships` evals gain a case for an invoice whose line amounts follow the invoice's currency, with the `lookup` path refused. No schema or runtime change.
19 changes: 19 additions & 0 deletions skills/objectstack-data/evals/objects-fields-relationships.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,25 @@
"sharing: { writeRequiresMasterRead"
]
}
},
{
"id": 18,
"prompt": "Each `invoice_line` belongs to an `invoice` (deleting the invoice deletes its lines), and the invoice carries `currency_code`. The line's `amount` must always be in its invoice's currency without storing a code on the line, and the invoice shows a subtotal of its lines. A teammate suggests making `invoice` a plain `lookup` instead. How do I declare the amounts?",
"expected_output": "Keep `invoice` a `master_detail` and declare `amount` with `currencyMode: 'record'` and `currencyField: 'invoice.currency_code'`: on a `master_detail` child the code may be one hop to the parent's `iso_4217_currency` field, so the line stores no code. A `lookup` path is refused, so the teammate's change would refuse the amount. The invoice's `subtotal` is a `summary` with `function: 'sum'` over the lines' `amount`, and it must declare its own `currencyMode: 'record'` with `currencyField: 'currency_code'`. A blank invoice code refuses the line's amount (`ERR_CURRENCY_CODE_REQUIRED`), and once a line follows it the invoice's `currency_code` cannot change until none does.",
"files": [],
"assertions": {
"must_contain": [
"master_detail",
"currencyField: 'invoice.currency_code'",
"iso_4217_currency",
"summary",
"ERR_CURRENCY_CODE_REQUIRED"
],
"must_not_contain": [
"currencyMode: 'fixed'",
"Field.lookup('invoice')"
]
}
}
]
}
8 changes: 4 additions & 4 deletions skills/objectstack-ui/rules/dashboards.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,13 +304,13 @@ defineDataset({
// measure `currency` → the field's `defaultCurrency` (`fixed` only) →
// tenant `localization.currency`; omit it for counts. A
// `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`
// unless grouped by its code field (a parent-path `currencyField` admits
// none, objectstack-data → Field Types → Currency: measure its `homeAmount`
// field or the parent's `summary`). A `fixed`
// 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.
// currency or drop one).

// The widget just selects the dimension by name:
const signedByMonth: DashboardWidget = { id: 'signed_by_month', type: 'line',
Expand Down
Loading