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). On a configuration submitted for approval, Opportunity.Amount / Quote.TotalPrice read the configuration’s own total — and on an amendment that is the amount the amendment books (the delta against the contract), so thresholds fire on what the deal is worth, not on the full amended configuration.
  • 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:

OperatorReads asNumeric?String?Notes
Equalslhs == rhsyesyesCase-insensitive for strings
Not_Equalslhs != rhsyesyes
Greater_Thanlhs > rhsyes—Skips if RHS null (see below)
Greater_Or_Equallhs >= rhsyes—Skips if RHS null
Less_Thanlhs < rhsyes—Skips if RHS null
Less_Or_Equallhs <= rhsyes—Skips if RHS null
Is_Nulllhs is nullyesyes
Is_Not_Nulllhs is not nullyesyes
Containslhs contains rhs—yesCase-insensitive
Starts_Withlhs begins with rhs—yesCase-insensitive
Ends_Withlhs ends with rhs—yesCase-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__c by name, written as either ${VarName} or bare VarName. 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 wantMode = Line Aggregate, set:
Approve if any line has > 20% discountCOUNT(*) where Discount__c Greater_Than 20, outer Greater_Or_Equal 1
Approve if total line value > $250kSUM(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 modeCOUNT(*) 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 > $50kMAX(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:

  1. Opportunity.Amount Greater_Than 100000 — no condition scope, inherits “Enterprise Tier” → only fires when Enterprise Tier is on the cart AND amount > $100k.
  2. 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.