|
| 1 | +--- |
| 2 | +'@objectstack/spec': minor |
| 3 | +'@objectstack/skills': minor |
| 4 | +--- |
| 5 | + |
| 6 | +feat(spec): `currencyConfig.homeAmount: { field, rateDate, rateField }` — a record-mode amount declares its stored home-currency amount by pointer (ADR-0144 D5) |
| 7 | + |
| 8 | +Clause-②: yes (widening) |
| 9 | +Metadata-protocol: added |
| 10 | + |
| 11 | +⛔ Not released alone: this ships in the same release as the write-time home-currency projection that fills these fields (ADR-0144 D9, release R2: "the `homeAmount` key never ships ahead of the engine"). |
| 12 | + |
| 13 | +An amount in `currencyMode: 'record'` can now declare where its value is stored after conversion into the organization's home currency (the `localization.currency` setting), which date picks the exchange rate, and where the rate used is stored. Consolidated totals then aggregate the home field like any other stored currency field. |
| 14 | + |
| 15 | +```ts |
| 16 | +fields: { |
| 17 | + currency_code: Field.text({ label: 'Currency', valueDomain: 'iso_4217_currency' }), |
| 18 | + invoice_date: Field.date({ label: 'Invoice date' }), |
| 19 | + amount: Field.currency({ |
| 20 | + label: 'Amount', |
| 21 | + currencyConfig: { |
| 22 | + currencyMode: 'record', |
| 23 | + currencyField: 'currency_code', |
| 24 | + homeAmount: { field: 'amount_home', rateDate: 'invoice_date', rateField: 'amount_rate' }, |
| 25 | + }, |
| 26 | + }), |
| 27 | + amount_home: Field.currency({ label: 'Amount (home)', readonly: true }), |
| 28 | + amount_rate: Field.number({ label: 'Rate used', readonly: true }), |
| 29 | +} |
| 30 | +``` |
| 31 | + |
| 32 | +- **Three required pointers.** When `homeAmount` is declared, `field`, `rateDate` and `rateField` must all be given, and none may be empty. There is no default, no injected column and no fixed field name. The document date is never taken from `created_at`. `homeAmount` is refused under `dynamic` and `fixed`. |
| 33 | +- **What the pointers name, refused at parse with a remedy and the object's candidates.** Each refusal is located at `fields.AMOUNT.currencyConfig.homeAmount.POINTER`. |
| 34 | + - `field` must name another `currency` field of the same object in `dynamic` mode (it displays in the home currency, so metadata never names that currency). No other amount may already store into it. |
| 35 | + - `rateDate` must name a `date` or `datetime` field. |
| 36 | + - `rateField` must name a `number` field. Two amounts may share it only when they have the same `currencyField` and `rateDate`. |
| 37 | + - The amount itself must be a stored `currency` field. A `formula` amount is refused. |
| 38 | +- **The home and rate fields are written by the platform only.** They must declare `readonly: true`, the existing mark that drops a client's write of the field, and no `expression`. |
| 39 | +- **Skills:** `objectstack-data/rules/field-types.md` teaches `homeAmount`. Its field names (`amount_home`, `amount_rate`) are examples, not a contract. |
| 40 | +- **Nothing to migrate.** `homeAmount` is new, and no existing config changes meaning. |
0 commit comments