Skip to main content

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" }
]
}
FieldDescription
codeIdentifies the rule.
filterWhich articles the rule applies to, by their metadata; blank applies to all. See Filters.
expressionThe calculation, in CEL. See Expressions.
descriptionFree 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 for field is value; field != 'value' when none is.
  • Combine with and, or, not and 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​

VariableTypeDescription
pricesmap of price field → amountThe price's named fields, e.g. prices.fixed, prices.cost, prices.catalog
salesamountThe 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
parentmap of metadata key → list of valuesThe 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.