Calculated field expressions
A calculated field is a stored scalar field whose value is produced by an expression instead of user input. The value is persisted; runtime writes ignore user-supplied values for calculated targets. You configure one expression per field, and the server infers whether it is a local calculation (same record) or an aggregate calculation (over child records).
Supported target field types: Integer, Decimal, and Money.
Display field, calculated field, or C#
Section titled “Display field, calculated field, or C#”These are separate contracts:
| Requirement | Use |
|---|---|
Extra read-time text in Table and Card surfaces, such as a Full name value rendered from {LastName} {FirstName} | A Display field. It adds no stored column and cannot be searched, filtered, sorted, edited, or used by business rules. |
| A stored value produced by a supported formula | A calculated field described on this page. The runtime owns and persists the target value. |
| A stored derivation that needs procedural logic or additional reads | A C# before-save mutation function. Backfilling existing records is a separate operation unless the C# workflow implements it. |
Display name and subtitle are presentation rules, not Display Fields or stored calculations.
Local expressions
Section titled “Local expressions”A local expression runs on one record and may reference fields from the same entity table only — no dotted reference paths.
Building blocks:
- numeric literals (
12.50), parentheses, unary+/-; - arithmetic:
+,-,*,/; - same-table field keys:
Qty,Price,Discount; - functions:
IF,COALESCE,ROUND,ABS,MIN,MAX(see the function reference).
Qty * PriceTotal * (1 - Discount / 100)IF(Qty > 0, Qty, 0)ROUND(Total * (1 - Discount / 100), 2)COALESCE(ManualTotal, Total)IF conditions use a deliberately small grammar: same-table numeric, money,
and boolean fields, numeric literals, TRUE/FALSE/NULL, comparisons,
AND/OR/NOT, and IS [NOT] NULL. Text operators, dates, references, and
child-table forms are not available inside calculation conditions.
Typing is strict: numeric combines with numeric, money with money, and one
IF branch may be NULL. Mixing money with plain numbers in one result is
invalid. Integer targets truncate decimal results on write.
Money behavior
Section titled “Money behavior”Money arithmetic keeps the source currency. Supported: money ± money, money × or ÷ number, number × money. Combining or comparing money values with different currency codes makes the calculation invalid for that record rather than silently converting.
Aggregate expressions
Section titled “Aggregate expressions”An aggregate expression runs from a parent record over its child records:
SUM(Positions, TotalPrice)COUNT(Positions, Id)- The first argument is an inverse-reference field on the parent entity,
paired with a
Referencefield on the child that points back to the parent. - The second argument is a primary-table field on that child definition.
SUM,AVG,MIN,MAXacceptInteger,Decimal, andMoneysources;COUNTwith the childIdcounts active rows, with another field it counts rows where that field is not null.- Archived child rows are excluded.
- Empty set:
SUMandCOUNTreturn0;AVG,MIN,MAXreturnNULL. - Money aggregates include only rows whose currency matches the result
currency (the target field’s base currency, or the source currency when the
target has none); any non-null row in another currency makes the result
NULLinstead of mixing currencies.
Not supported: filtered aggregates (SUM(Positions, Amount WHERE …)), path
syntax (SUM(Positions[Order].TotalPrice)), collection paths outside
aggregate functions, and arbitrary code.
Evaluation order
Section titled “Evaluation order”Within one save the runtime applies, in order: mutation effects → local calculated fields → persist → aggregate recalculation on affected parents → local calculated fields on those parents. This supports chains such as:
OrderPosition.Price <- mutation effect copies Item.PriceOrderPosition.TotalPrice <- Qty * PriceOrder.Total <- SUM(Positions, TotalPrice)Order.TotalWithDiscount <- Total * (1 - Discount / 100)Form previews show local calculated values as you edit; aggregate values are server truth after save.
Authoring workflow
Section titled “Authoring workflow”For an API-first workflow, use the Configuration API served by the target installation:
- Read the current field, including
CalculationExpression,CalculationKind, andRowVersion:GET /api/workspace/admin/entity-fields/{fieldId}. - Validate an expression without saving:
POST /api/workspace/admin/entity-fields/{fieldId}/calculation/validatewith{"Expression":"Qty * Price"}. - After validation succeeds, save exactly that expression with the current
row version:
PUT /api/workspace/admin/entity-fields/{fieldId}/calculationwith{"Expression":"Qty * Price","RowVersion":"<current-guid>"}.
The generated calculation validate operation — errors include the request field, localized message, and stable code; a valid result also reports the inferred calculation kind (local or aggregate). Then set or change the expression through the field’s calculation operation in the Configuration API. Invalid expressions are rejected with the same structured errors. Fields referenced by a calculation become schema dependencies: they block deletion and incompatible type changes until the calculation is updated.