xMatix
Sign in Request demo
xMatix
PRODUCTS
SalesField SalesCRMRewardsClaimsInventoryProcurementWarehouse ManagementField ServiceServiceSupportTelephony & MessagingFinance & AccountingPayrollExpense ManagementCommercePortalsAnalytics & ReportingData StudioMobile AppSee all products →
PLATFORM
Platform overviewApp BuilderAutomationIntegrationsSecurity & GovernanceChange ManagementDevelopers
SENSE AI
Sense AI overviewSense AssistSense ControlSense VisionAI StudioTrust & governanceIn Claude & ChatGPTUse cases
SOLUTIONS
FMCG & DistributionManufacturing & Dealer NetworksAutomotive & DealershipsPharma & HealthcareConsumer DurablesAgri-InputsBuilding MaterialsService NetworksWarehousing & 3PLFinancial AccountingERP SoftwareIndia GST ComplianceUAE VAT & e-InvoicingSaudi ZATCA & VATAll solutions →
RESOURCES
Knowledge CenterDeveloper & CLIBlogGuidesWhat is xMatix?Company facts
COMPANY
AboutCareersPartnersEventsContactAuthorsLegal
Sign in Request demo
Home/Docs/Customization/Expressions: formulas and query filters
REFERENCE · Last reviewed

Expressions: formulas and query filters

Small text expressions appear all over xMatix customization — default value formulas, validation conditions, business-rule logic, view filters, lookup filters — and they belong to two different languages with two different runtimes. Formula expressions are evaluated in memory against a record during load and save; query filters are translated into the database query that selects records for a list or a picker. The two look similar and are not interchangeable: an expression that works in a view filter can fail in a default formula and vice versa, and knowing which surface you are on is most of the skill. This page is the reference for where each language is used and what each supports.

The two surfaces at a glance

Formula expressionsQuery filters
EvaluatedIn memory, against one record, during load and saveIn the database, against many rows, when a list or picker runs
Used byField default formulas, calculated fields, validation conditions, business-rule conditions and action expressions, approval entry conditionsSaved view filters and sort orders, lookup filter conditions, end-user list filters
SeesThe record's fields, related records via navigation, the current user and environmentThe queried entity's rows; contextual values (host record, user, environment) substituted in where the surface supports them
FailsAt evaluation — and on some surfaces silently (see below)At run time, when the list or picker executes — not when the expression is saved

Formula expressions

Formulas run against a materialized record, which is what gives them their reach: they read the record's fields by name, follow lookups through dotted navigation paths (Account.Region, up to five hops, read-only), and — on the surfaces that allow it — aggregate over the record's own child collections. They return a value (for defaults, calculated fields and rule value expressions) or a boolean (for conditions).

The syntax essentials:

  • Field names are identifiers: Status, TotalAmount, Account.CreditLimit.
  • String literals must be quoted"Draft" — because a bare word is read as a field reference. An unquoted literal is the single most common broken formula.
  • Comparisons (==, !=, >, <, >=, <=) combine with and, or and not (&&, ||, !).
  • Picklist comparisons use the option's stored value, never its display label — a condition comparing against a value outside the field's domain can never be true, and nothing warns you.
  • Date and time come from the environment context (for example, today's date for stamping and comparing), and the current user is available on surfaces that support user context.
  • Results are coerced to the target field's type where the formula feeds a field; a value the type cannot hold surfaces as a diagnostic, not a crash.

Aggregates over child records exist in two spellings — function style, SUM(OrderLines, UnitPrice * Quantity) with COUNT, AVG, MIN, MAX and ANY, and an equivalent lambda style — with the per-element body evaluated against each child. Their availability is the sharpest difference between formula surfaces, so it is worth stating exactly:

SurfaceNavigation pathsChild aggregates
Business-rule conditions and value expressionsYesYes — over the record's own collections, by relation name
Validation conditionsYesNo — a condition needing "sum of the lines" belongs in a rule
Field default formulasFor reading, yesNo — defaults are same-record
Calculated fieldsYesNo — calculated fields are same-record math; cross-record rollups are a business-rule job

Two further limits hold everywhere: aggregates work only on the record's own collections, not on collections reached through navigation (Account.Orders cannot be summed), and formulas never write through a navigation path — targets are always local fields. Anything past these limits is automation-script territory.

The failure mode to respect: on some surfaces an unparseable formula fails silently. A business-rule condition that does not parse quietly disables the rule — the save proceeds as if the rule did not exist — and the only evidence is a diagnostic in the rule's Test Runner. Preview after every edit; the discipline is in the business rules procedure.

Query filters

Query filters select rows: a saved view's filter and sort order, a lookup filter's condition over the target entity, and the ad-hoc filters users apply to lists. Because they execute as part of the database query, they are written against the queried entity's fields and stay within what a query can express — comparisons, and/or/not combinations, and sorting. A view's filter is combined with whatever the user filters at run time; a lookup filter is combined with the user's search term.

Contextual values reach a query filter by substitution, and the lookup-filter surface is the richest: its condition can reference the host record's current in-form values (match the picked record's region to the record being edited, live, as the user types), the current user (id, email, whether the user is external — useful for "my records" and partner scoping), and environment values such as the current time. The condition itself always runs against the target entity's rows.

Query filters fail late: a filter naming a nonexistent field saves without complaint and breaks when the view or picker runs — and an over-tight lookup filter does not error at all, it just produces an empty picker. Always run the view and open the picker once after authoring, with a record that should match.

Choosing the right surface

Ask what the expression is for. Deciding something about one record — a default, a computed value, a check, a rule condition — is a formula. Selecting which records appear — in a list, a picker, a view — is a query filter. The distinction explains the differing power: formulas can navigate and aggregate because they hold one whole record in memory; query filters stay lean because they run inside queries over many rows. When an expression cannot say what you need in either language — multi-statement logic, external data, writes across records — the answer is not a cleverer expression but the next rung: an automation script, which is full C# rather than an expression at all.

Common questions

Why did my expression work in a view filter but fail as a default formula?

Because they are different languages on different runtimes — the query-filter grammar the view accepted is not the formula grammar the default evaluates. Rewrite for the surface rather than pasting between them, and re-test on the destination surface: the overlap (simple comparisons) is large enough to lull you, and the differences (aggregates, context tokens, coercion) bite exactly when the expression matters.

How do I show the sum of child lines on a record?

With a business rule: a value-setting action whose expression aggregates the record's own child collection — SUM(OrderLines, Amount) — into a regular field on save. Calculated fields will not do it (they are same-record math), and there is no rollup field type. The stored total then behaves like any field: visible on layouts, filterable in views, usable in conditions. Remember it recomputes when the parent saves — a rule on the child entity that touches the parent keeps it current when lines change independently.

Why can't my condition see the record's previous value?

Formula conditions evaluate the incoming record — there is no "old value of Status" token. Comparing before and after is what the business-rule transition trigger is for: it matches on the actual from/to movement of a field, and the condition then qualifies the new record. In scripts, both old and new values are available on the dispatched records; in plain expressions, design around the trigger instead.