Price Rules
Price rules compute a pricelist's sales price from the price fields you store: a markup on cost, a discount on the catalog price, a different calculation for one brand. They are small expressions, not a scripting language, so they are predictable and give the same result every time.
Defining Rules
Rules are part of the pricelist, in price_rules:
{
"code": "wholesale-se",
"currency_code": "SEK",
"vat_mode": "NET",
"parent_code": "base-se",
"price_rules": [
{ "code": "acme-brand", "filter": "brand = 'acme'", "expression": "prices.cost * 1.25", "description": "Acme at cost + 25 %" },
{ "code": "default", "filter": "", "expression": "sales * 0.85", "description": "15 % off" }
]
}
| Field | Description |
|---|---|
code | Identifies the rule. |
filter | Which articles the rule applies to, by their metadata; blank applies to all. See Filters. |
expression | The calculation, in CEL. See Expressions. |
description | Free text. |
For each price, rules are tried in order. The first rule whose filter matches the article and whose expression yields a number sets the sales price. If none does, the price keeps the sales price it came in with: the fixed field for a price written on the pricelist, the parent's price for an inherited one.
Filters
A filter selects articles by their metadata:
brand = 'acme'
category = 'chairs' and brand != 'acme'
(season = 'summer' or season = 'clearance') and not outlet = 'yes'
field = 'value'matches when any of the article's values forfieldisvalue;field != 'value'when none is.- Combine with
and,or,notand parentheses. Keywords are case-insensitive; values are in single quotes. - A blank filter matches every article.
Rules apply only to SKUs that exist as articles. A price for a SKU without an article gets its fixed field as the sales price.
Expressions
Available variables
| Variable | Type | Description |
|---|---|---|
prices | map of price field → amount | The price's named fields, e.g. prices.fixed, prices.cost, prices.catalog |
sales | amount | The sales price going into the rule: for a price written on this pricelist, its fixed field; for an inherited price, the parent's sales price |
parent | map of metadata key → list of values | The parent pricelist's metadata, when the pricelist has a parent |
An expression that yields a number sets the sales price. Anything else (null, true, a string) means the rule does not apply, and the next rule is tried. An expression that fails, for example by reading a price field the price does not have, stops rule processing for that price, and it keeps the sales price it came in with.
Examples
prices.cost * 1.35 // 35% markup on cost
prices.catalog * 0.90 // 10% discount on catalog
sales - 5 // 5 off
prices.fixed > 100.0 ? prices.fixed * 0.95 : prices.fixed // 5% off for items over 100
parent['segment'][0] == 'vip' ? sales * 0.9 : sales // 10% off under a VIP parent
On an inherited price, sales * 0.9 takes 10 % off the parent's price, so rules on several levels of a hierarchy compound: 100 on the base, 90 on a segment with sales * 0.9, 81 on a customer under it with sales * 0.9. To start from a stored field instead, use prices.<field>. See Inheritance.
Amounts are exact decimals: prices.fixed / 3 and sales - 5 have no floating-point noise, and prices.a == prices.b is true whenever the two are numerically equal (100 and 100.00).
An expression that does not compile, or that uses a variable other than prices, sales and parent, is rejected when the pricelist is saved. Other mistakes, such as comparing an amount with a string, show only when a price is calculated: rule processing then stops and the price keeps the sales price it came in with. Check a new rule with a test query.
When Rules Take Effect
- New prices: a price gets the rules in force when it takes effect.
- Changed rules: the pricelist's own prices and the prices it inherits are recalculated with the new rules from the moment the change takes effect, now or at its
valid_from. Prices before that moment keep the rules they had. See Historical Pricing.
What Rules Do Not Do
Rules work on one price at a time. They do not:
- read other SKUs' prices, external data or the time of day;
- depend on the query (customer, quantity, currency). Choose the customer's pricelist in the query instead, and use quantity breaks for volume pricing;
- handle date ranges. Schedule prices with a start date instead; see Scheduled Updates.
Summary
A pricelist's rules turn the price fields you store into its sales price. A metadata filter picks the articles, a CEL expression does the calculation, and the first matching rule wins.