Firing criteria
A condition is a single comparison: a left-hand value, an operator, and
a right-hand value. The engine evaluates each condition in the rule, then
combines them using the rule’s ConditionsMet setting (All / Any / Custom).
Tested object + tested field
What value does the left-hand side read? The picker offers:
- Quote — fields on the parent Quote (Quote-mode orgs only).
- Opportunity — fields on the parent Opportunity (both modes).
- Account — fields on the deal’s Account.
- ConfigurationOption — scalar fields on the in-flight configuration
draft:
TotalPrice,ProductCount,OptionName,PricingModel,Status. - Product — any line on the deal carries this product (any-match semantics; fires if at least one product on the cart matches).
- Bundle Option — any bundle option in the configured tree matches.
Operators
The picklist mirrors the pricing rule operator vocabulary so admins who already author Price Conditions feel at home:
| Operator | Reads as | Numeric? | String? | Notes |
|---|---|---|---|---|
Equals | lhs == rhs | yes | yes | Case-insensitive for strings |
Not_Equals | lhs != rhs | yes | yes | |
Greater_Than | lhs > rhs | yes | — | Skips if RHS null (see below) |
Greater_Or_Equal | lhs >= rhs | yes | — | Skips if RHS null |
Less_Than | lhs < rhs | yes | — | Skips if RHS null |
Less_Or_Equal | lhs <= rhs | yes | — | Skips if RHS null |
Is_Null | lhs is null | yes | yes | |
Is_Not_Null | lhs is not null | yes | yes | |
Contains | lhs contains rhs | — | yes | Case-insensitive |
Starts_With | lhs begins with rhs | — | yes | Case-insensitive |
Ends_With | lhs ends with rhs | — | yes | Case-insensitive |
Value source
What value does the right-hand side read?
- Literal. Whatever you typed.
20,Enterprise,true. Coerced numerically when both sides parse as numbers. - Field Reference. A scope-qualified field path like
Account:Discount_Threshold__c. Reads the same context map the LHS uses, so you can compare two fields on the same deal. - Variable. A reference to an
ApprovalVariable__cby name, written as either${VarName}or bareVarName. See Approval variables.
Combining conditions
Each rule has a ConditionsMet setting:
- All. Every condition must pass. Default. Use for AND semantics.
- Any. At least one must pass. Use for OR semantics.
- Custom. A boolean expression like
(1 AND 2) OR 3, referring to conditions by their 1-indexed position. If the expression is unparseable, the engine falls back to All and logs the fallback in the trace.
Aggregate conditions
A single condition like “discount > 20%” only reads ONE field on ONE record. To answer “how many bundle lines are in roll-up mode?” or “what’s the total margin across all lines?”, switch the condition’s Mode picker from Field to Line Aggregate.
The sub-editor exposes:
- Aggregator —
COUNT/SUM/AVG/MIN/MAX. - Aggregator Field — the line-item field the aggregator operates on
(ignored for
COUNT). Schema-driven picker: filtered to numeric fields when the outer operator is numeric, otherwise unconstrained. - Filter Field / Operator / Value — an optional WHERE clause that decides which line items participate. Same operator picklist as field-mode.
- Outer Operator + Value — compares the aggregate result. Forced to numeric operators because every aggregator returns a number.
Worked examples.
| You want | Mode = Line Aggregate, set: |
|---|---|
| Approve if any line has > 20% discount | COUNT(*) where Discount__c Greater_Than 20, outer Greater_Or_Equal 1 |
| Approve if total line value > $250k | SUM(TotalPrice), no filter, outer Greater_Than 250000 |
| Approve if average margin under 30% | AVG(Margin__c), no filter, outer Less_Than 30 |
| Approve if any bundle is in Roll-Up mode | COUNT(*) where PricingModel Equals Roll-Up, outer Greater_Or_Equal 1 |
| Approve if minimum line margin under 10% | MIN(Margin__c), no filter, outer Less_Than 10 |
| Approve if largest single-line price > $50k | MAX(TotalPrice), no filter, outer Greater_Than 50000 |
Empty result sets resolve to 0 for every aggregator (so an empty cart never fires a SUM-over-threshold rule by accident). Non-numeric aggregator fields log a trace note and resolve to 0 as well.
Approval variables
A variable is a named expression you author once. Conditions reference it by name; the engine resolves the expression against the deal at submit time.
When to factor a value out:
- The same threshold appears in three rules (“$50k” duplicated; if the threshold changes, you’d edit three places).
- The value lives on the Account, not the Quote — e.g.
Account.Discount_Threshold__c— and several rules want it. - The math is non-trivial — a formula crossing two objects.
Author variables in Pinion Admin → Approvals → Variables. Each record has:
- Name — referenced from conditions as
${Name}. - Description — surfaced in the wizard picker so the right one gets chosen.
- Formula — a dotted field path (e.g.
Quote.Account.Discount_Threshold__c) or constant expression. - Return Type —
Number/Currency/Percent/Text/Boolean. Drives operator compatibility.
Scope Product
A rule (or a single condition) can be scoped to a product so it only fires when that product is on the deal. This is the difference between “any high-value deal needs CFO” (no scope) and “any deal with our Enterprise SKU needs Product Marketing” (scoped).
The rule’s ScopeProduct__c field is the rule-level scope. When set,
the engine pre-filters: if the deal doesn’t carry the product, the rule
is skipped before its conditions even evaluate. Bundle parents count —
a child line whose ParentBundle__c.Product2Id matches the scope
product satisfies the gate. Non-bundle products match on direct
Product2Id.
Conditions have their own ScopeProduct__c for per-condition overrides.
Inheritance rule: the effective scope is
condition.ScopeProduct ?? rule.ScopeProduct — a condition without an
override inherits the rule’s, but a condition with its own override
replaces (not narrows) the rule’s. Use this when most conditions
on a rule share a scope but one condition needs a different product
(rare; usually a sign you want two rules).
Worked example. A rule scoped to “Enterprise Tier” with two conditions:
Opportunity.Amount Greater_Than 100000— no condition scope, inherits “Enterprise Tier” → only fires when Enterprise Tier is on the cart AND amount > $100k.Product.Quantity Greater_Than 10— same inheritance. Reads “any Product on this cart whose quantity > 10,” gated to deals carrying Enterprise Tier.
If you instead want condition 2 to scope to “Starter Tier” — same rule,
different product — set its ScopeProduct__c to Starter Tier and the
rule keeps Enterprise.
Related
- Authoring a Rule — where conditions attach to a step.
- Approvers — who signs off once conditions match.
- Runtime & Troubleshooting — reading the condition trace.