Field.formula({ scale: 2 }) is not applied: a percentage formula stores and returns 41.666666666666664, and the record page prints all 15 digits
Measured on 17.1.0 (open edition, objectstack dev), HotCRM's crm_campaign.
The declaration
src/objects/campaign.object.ts:
response_rate: Field.formula({label: 'Response Rate %',expression: F`coalesce(record.num_sent, 0) > 0 ? (coalesce(record.num_responses, 0) * 100.0) / record.num_sent : 0.0`,scale: 2,}),What the runtime returns
GET /api/v1/data/crm_campaign/<id> with num_sent: 12, num_responses: 5:
{"response_rate": 41.666666666666664, "roi": 400, "num_sent": 12, "num_responses": 5}scale: 2 is declared and has no effect — the raw double is what is stored/returned, so every consumer inherits it. The record page renders it verbatim:
效果数据
响应率(%)
41.666666666666664
That is what an end user sees on the campaign page, next to 预算成本 28,000 and 投资回报率(%) 400. It reads like a bug in the product to them, and there is nothing an app author can do about it declaratively: they did declare the scale.
Why it matters beyond cosmetics
A formula field's scale is the only declarative rounding an author has. If it is inert:
- every division-shaped formula (rates, ratios, per-unit prices, averages) is a coin flip on how it prints;
- values that flow onward into a stored money/decimal field hit that field's own decimal validation and get rejected — the same tail that makes
x * (d/100) unwritable to a 2-decimal money column. So an inert scale is not only display noise, it can turn a downstream write into a hard failure.
Expected
Either the engine applies scale when evaluating the formula (round-half-up to N decimals before returning/persisting), or scale is rejected at author time on Field.formula so authors know they must round inside the expression. Silently accepting a rounding declaration and not rounding is the shape that costs an app author a production incident.
crm_campaign.roi carries the same scale: 2 and happens to divide evenly in the seed data, so it looks fine — that is the accident, not the rule.
Field.formula({ scale: 2 })is not applied: a percentage formula stores and returns41.666666666666664, and the record page prints all 15 digitsMeasured on
17.1.0(open edition,objectstack dev), HotCRM'scrm_campaign.The declaration
src/objects/campaign.object.ts:What the runtime returns
GET /api/v1/data/crm_campaign/<id>withnum_sent: 12,num_responses: 5:{"response_rate": 41.666666666666664, "roi": 400, "num_sent": 12, "num_responses": 5}scale: 2is declared and has no effect — the raw double is what is stored/returned, so every consumer inherits it. The record page renders it verbatim:That is what an end user sees on the campaign page, next to
预算成本 28,000and投资回报率(%) 400. It reads like a bug in the product to them, and there is nothing an app author can do about it declaratively: they did declare the scale.Why it matters beyond cosmetics
A formula field's
scaleis the only declarative rounding an author has. If it is inert:x * (d/100)unwritable to a 2-decimal money column. So an inertscaleis not only display noise, it can turn a downstream write into a hard failure.Expected
Either the engine applies
scalewhen evaluating the formula (round-half-up to N decimals before returning/persisting), orscaleis rejected at author time onField.formulaso authors know they must round inside the expression. Silently accepting a rounding declaration and not rounding is the shape that costs an app author a production incident.crm_campaign.roicarries the samescale: 2and happens to divide evenly in the seed data, so it looks fine — that is the accident, not the rule.