Documentation Index Fetch the complete documentation index at: https://docs.getveles.com/llms.txt
Use this file to discover all available pages before exploring further.
Formula Rules are the most flexible rule type in Veles. They use a custom expression language (similar to Excel or SQL) to evaluate conditions and calculate values that go beyond what the standard rule builder can express.
Use Formula Rules when your logic involves multiple variables, conditional branching, list aggregation, or lookups against Data Sheets . If you can express it as a formula, you can build it as a rule.
Formula Rules are a power-user feature. For straightforward guardrails (discount caps, required products, price floors), Quote Validation , Row Validation , and Pricing Rules are simpler to configure. Use Formula Rules when those standard kinds can’t express the logic you need.
Formula Rules are the right choice when:
The condition involves multiple variables combined (e.g., TCV > $500k AND term < 24 months AND segment = Enterprise)
You need conditional branching based on segment, region, or product tier (CASE/IF logic)
The logic requires aggregating across line items (e.g., sum the price of all “Enterprise” tier products)
You need to look up values from a Data Sheet at evaluation time (VLOOKUP)
The calculation involves date or duration math (e.g., contract length thresholds)
Navigate to Admin Console > Price Books > Rules .
Click Create New Rule .
Set the Kind to Formula Validation .
Enter a Name and Description .
Set the Status to Active.
Click Save , then open the formula editor to write your expression.
The formula editor provides syntax highlighting and validation. The formula must evaluate to a boolean (true/false) for validation rules, or a numeric value for pricing adjustments.
Language basics
Data types
Type Syntax Examples Numbers Integers, decimals, scientific notation, hex 100, 15.5, 1e-3, 0x1AStrings Single or double quotes 'Enterprise', "USD"Booleans Lowercase keywords true, falseNull Keyword nullArrays Braces {1, 2, 3}Dates DD-MM-YYYY format 22-01-2026Durations Duration keyword duration(5, days), duration(12, months)
Operators
Evaluated in order of precedence (highest first):
Category Operators Description Unary +, -, %Positive/negative signs and percentage (e.g., 50% = 0.5) Power ^Exponentiation Multiplicative *, /Multiplication and division Additive +, -Addition and subtraction Relational <, <=, >, >=, =, !=, <>Comparisons (return true/false) Logical &&, `` AND, OR
Use parentheses to override precedence when needed: (a + b) * c.
Variables
Variables reference real-time data from the quote being built. They’re organized by scope.
Quote-level variables
Variable Type Description quote.segmentString The segment name (e.g., “Enterprise”, “SMB”). quote.segment_idString The unique UUID for the segment.
Option-level variables
Variable Type Description option.frequencyInteger Billing cycles per year. 1 = annual, 4 = quarterly, 12 = monthly. option.currency_codeString 3-letter currency code (e.g., “USD”, “EUR”). option.term_lengthInteger Contract length in months. option.product_skusList Array of all SKUs in the option. option.promotion_countInteger Number of promotions applied.
Phase-level variables
Variable Type Description phase.totalNumeric Net price after discounts (annualized). phase.discountNumeric Overall discount percentage (0-100). phase.list_priceNumeric Total price before discounts. phase.yearInteger Current year in a multi-year deal (1-indexed). phase.linesList Array of line item objects. Used for advanced filtering and aggregation.
Control flow
IF function
For simple true/false branching:
if(condition, true_value, false_value)
Example: Return a different discount cap based on billing frequency:
if(option.frequency = 1, 25, 15)
This returns 25 (%) for annual billing and 15 (%) for everything else.
CASE expression
For multi-branch logic, use CASE. It evaluates conditions sequentially and returns the first match:
CASE
WHEN quote.segment = "Enterprise" THEN 0.20
WHEN quote.segment = "Mid-Market" THEN 0.10
ELSE 0.05
END
This returns the maximum allowed discount based on the customer’s segment.
List processing
One of the most powerful features of the formula engine is the ability to filter, transform, and aggregate lists of line items.
FILTER
Returns a subset of a list based on a condition:
FILTER(phase.lines, line, line.category = "Software")
This returns only the Software line items from the current Phase.
MAP
Transforms every item in a list:
MAP(phase.lines, item, item.price)
This extracts the price from each line item, producing a list of numbers.
SUM
Aggregates a list of numbers:
Returns 600.
Chaining: FILTER + MAP + SUM
The real power comes from chaining these together. For example, summing the price of all Enterprise-tier products in a Phase:
SUM(
MAP(
FILTER(phase.lines, line, line.product.custom_attributes.tier = "enterprise"),
item,
item.price
)
)
This filters to Enterprise-tier lines, extracts their prices, and sums the result.
Data Sheet lookups
Use VLOOKUP to query Data Sheets from within a formula:
vlookup(sheet_name, lookup_column, lookup_value, return_column)
Example: Look up the maximum discount for the customer’s segment from a Discount_Thresholds Data Sheet:
vlookup("Discount_Thresholds", "Segment", quote.segment, "Max_Discount")
If the Data Sheet has a row where Segment = “Enterprise” and Max_Discount = 25, this returns 25.
You can then use the looked-up value in a condition:
phase.discount <= vlookup("Discount_Thresholds", "Segment", quote.segment, "Max_Discount")
This formula evaluates to true when the discount is within the segment’s allowed maximum, and false when it exceeds it.
Function library
Math and logic
Function Usage Description abs(n)abs(-42) → 42Absolute value. round(n, digits)round(3.456, 2) → 3.46Round to decimal places. sqrt(n)sqrt(16) → 4Square root. min(a, b, ...)min(10, 20, 5) → 5Minimum value. max(a, b, ...)max(10, 20, 5) → 20Maximum value. avg(list)avg({10, 20, 30}) → 20Average of a list.
String manipulation
Function Usage Description concat(a, b, ...)concat("Hello", " ", "World") → "Hello World"Join strings. contains(text, search)contains("Enterprise", "Enter") → trueCheck if text contains a substring. left(text, n)left("Enterprise", 3) → "Ent"Extract from the left. right(text, n)right("Enterprise", 5) → "prise"Extract from the right. substitute(text, old, new)substitute("Hello World", "World", "Veles")Replace text.
Date and duration
Function Usage Description duration(n, unit)duration(12, months)Create a duration for comparison.
Example: Check if the contract term meets a minimum threshold:
option.term_length >= duration(12, months)
End-to-end examples
Segment-based discount validation
Validate that the discount doesn’t exceed the maximum for the customer’s segment, using a Data Sheet lookup:
phase.discount <= vlookup("Discount_Thresholds", "Segment", quote.segment, "Max_Discount")
Action if false: Warn, “Discount exceeds the maximum for this segment. Requires approval.”
Approval: Tier 2: Deal Desk
Multi-variable deal gate
Require VP approval when a deal has high value, short term, and deep discount:
NOT (phase.total > 500000 && option.term_length < 24 && phase.discount > 15)
Action if false: Warn, “Large short-term deal with significant discount. Requires VP review.”
Approval: Tier 3: VP of Sales
Year-over-year ramp validation
In a multi-year deal, ensure Year 2 pricing is not lower than Year 1:
if(phase.year = 1, true, phase.total >= phase.list_price * 0.95)
Action if false: Error, “Year 2+ pricing cannot be more than 5% below list price.”
Enterprise product concentration
Warn when Enterprise-tier products represent more than 80% of the deal value (over-concentration risk):
SUM(MAP(FILTER(phase.lines, line, line.product.custom_attributes.tier = "enterprise"), item, item.price)) / phase.total <= 0.80
Action if false: Warn, “Enterprise products represent more than 80% of deal value. Review product mix.”
Tips
Start with validation rules, graduate to formulas. If you can express the logic with the standard Quote Validation or Row Validation builder, do that. Formula Rules are more powerful but harder to debug and maintain. Use them when the standard builder genuinely can’t handle the logic.
Use VLOOKUP instead of hard-coding. Any time you’re tempted to put a specific number in a formula (a threshold, a rate, a cap), consider putting it in a Data Sheet and looking it up with VLOOKUP. This makes the formula reusable across segments and easier to update.
Test with edge cases. Formulas can produce unexpected results with null values, empty lists, or zero-division scenarios. Test with quotes that have one line item, zero discount, and unusual product combinations.
Document your formulas. Use the rule’s Description field to explain the business logic in plain language. A formula like SUM(MAP(FILTER(...))) is opaque without context. The description should say what it does in business terms.
What’s next
Data Sheets Build the reference tables that VLOOKUP queries against.
Getting Started with Rules Overview of the rules engine and simpler rule kinds.
Pricing Rules Automatic price adjustments without formula syntax.
Translating SFDC Rules Map Salesforce CPQ rules to the Veles formula engine.