Formulas & calculations
A computed field carries a formula instead of an input. The server evaluates it on every save and every read — a browser cannot submit a total of its own choosing, and a stale total is never served.
Writing a formula
Reference other fields by slug in backticks, and combine them with ordinary arithmetic and comparisons:
`ticketCount` * `ticketPrice`
`amount1` + `amount2` + `amount3`
40 + (`tier` - 1) * 25 + MAX(0, `tier` - 2) * 60
Operators
| Operators | Meaning | Binds |
|---|---|---|
* / % | Multiply, divide, remainder | Tightest |
+ - | Add, subtract (and unary minus) | ↓ |
> < >= <= | Comparison | ↓ |
= != | Equal, not equal | ↓ |
AND | Both hold | ↓ |
OR | Either holds | Loosest |
Equality is a single =, not ==. AND and
OR are words, in capitals. Brackets group as you would expect, and when in doubt
they cost nothing.
Functions
The function set is deliberately closed: MAX, MIN,
ROUND, ABS, FLOOR and CEIL. Formulas are
parsed and executed by a small purpose-built evaluator — never handed to a scripting engine —
so a formula is data, and there is no formula that can run code.
There is no IF. Conditional behaviour is expressed as a rule with conditions
rather than a branch inside an expression, which keeps the two readable separately — and
arithmetic often does the job on its own, as the MAX(0, …) line above does.
Text and numbers
String literals use quotes — `status` = "approved" — and comparisons work on
text as well as numbers. A numeric value that arrived as text is understood as a number, so a
dropdown whose values are "1", "2", "3" can be
arithmetic on without conversion.
What the engine does with missing values
A field the submitter has not reached yet is absent, and a formula referencing an
absent field is not computable — it produces nothing rather than a confident total
derived from half a form. Give optional numeric fields a default of 0 when a
running total should always show.
A hidden field is different from an unfilled one, deliberately: hiding a field
neutralises its value to zero or empty, so base + addOn with a hidden
addOn yields base — the field contributes nothing, but the formula
still computes.
When a formula stops being computable, its recorded value is cleared rather than left standing. A figure on screen is always this pass's answer, never a fossil of the last one.
Rules: everything else logic can do
A formula on a field is the short form. The logic builder holds rules — conditions, and what happens when they hold. The full set:
| Effect | What it does |
|---|---|
| Set a variable | Assigns an expression's result to a computed field. |
| Format a value | Writes a presentation string — currency, whole numbers — into a computed field. |
| Show / Hide | Reveals or hides fields. Hidden fields are not required and contribute nothing to calculations. |
| Set an outcome | Raises a named flag with a message — “Needs approval”, “Refer to underwriting”. |
| Set the workflow state | Moves the submission directly, letting logic drive the state machine. |
| Send | Queues a notification or a document. |
| Reference number | Allocates from a sequence — see below. |
| Send to an integration | Queues a delivery to a named connection. |
| Deliver objects | Hands the submitter files. |
| Collect payment | Prices the submission — see payments. |
Note which of these leave the platform — sending, integrations, delivery, payment. Those are recorded during evaluation and performed only when a submission is really saved, because evaluation also runs on previews, on read, and on failed validation. Looking at a form never emails anybody.
Reference numbers
A reference number is allocated exactly once, from a sequence you define, at the moment a submission is first committed — and never re-allocated. Editing a record does not renumber it, because evaluation only ever asks for a number; the allocation happens on the save path, under a lock, so two simultaneous saves cannot collide on one.
Server-owned, always
Submitted data and computed data are stored separately and never merged. Every write re-runs the engine; every read recomputes for display. Two recorded exceptions exist because recomputing must not mean forgetting: a reference number is allocated exactly once, and a value a workflow transition set stays set. A live formula always wins over either.
Prove it before you publish
Form test cases pin named inputs to expected outputs and re-run through the real engine —
run them against a draft to answer the only question that matters before publishing:
would this change a value somebody pinned down? Comparison is canonical for numbers
(42, 42.0 and "42" agree) and asserts only the keys
each case names, so adding an unrelated field never breaks the suite.
Test cases are owner-only authoring tools: a partner an organisation shared a form with uses the form rather than maintaining its tests.