Recipes

The recipe language describes how fields (or more generally: variables) are evaluated. In the context of Brezel, recipes bind a field’s value to the values of other fields that are referenced in the recipe. For example, it allows you to automatically calculate totals in invoices, define field values inside workflows, and even evaluate expressions inside bakery planner configuration files.

Syntax

The syntax of the recipe language is based on the EcmaScript syntax with a few additions. Supported are mathematical, logical and bitshift operations, function calls, property, subscript and method access and of course common literals.

Math and logic

Say we want a field total to always be the sum of two other fields net and tax. Then we define the recipe of total to be:

net + tax

If we do not know the total tax but the tax rate, we write something like:

net * (1 + tax_rate)

and if we know that the tax rate is constant, we can of course write:

net * 1.19

These operations work the same way you are probably accustomed to from other languages or Excel. We can also test for truth values:

net > 10

would return either true or false. We can also negate the result:

!(net > 10)

Testing for equality is done using ==:

net == 10

And its negation

!(net == 10)

or

net != 10

This would return true if and only if net is not equal to 10.

We can also test multiple conditions:

net < 10.25 && net > 5.75

Would return true if net is below 10.25 and greater than 5.75.

Available operators: + (add), - (subtract), * (multiply), / (divide), > (greater than), >= (greater than or equal), < (less than), <= (less than or equal), == (equals), != (not equals), ! (not), && (and), || (or), & (bitwise and), | (bitwise or), ^ (bitwise xor), >> (right bitshift), << (left bitshift).

String operations

Recipes are not limited to numbers. If we have two fields first_name and last_name but want the field full_name to be automatically calculated, we define the following recipe:

first_name + " " + last_name

Note that we can also use single quotes instead:

first_name + " " + last_name

Properties

Fields can not only be numbers or text, but also objects that have properties. Fields of type select for example hold relations to other Brezel entities. Say we want to define a field total that depends on the price of the selected product product and another field quantity. Then we can write the following recipe:

product.price * quantity

Subscript syntax works too in this case:

product["price"] * quantity

Functions

Often, we want to transform values in some way. There are pre-defined functions available for that, for example when we want to round numbers for a currency field:

round(product.price * quantity)

Available functions are listed here .

Conditionals

Ternary operator

Sometimes we want the value to be something in one case, but be something different in another case. Then we can write:

condition ? x : y

If the condition is true, it will return x, otherwise it will return y. Example:

category == "Food" ? 0.07 : 0.19

will return 0.07 if the category in question is Food, otherwise we get 0.19.

Null-coalescing operator

If you just want the resulting expression if it does not evaluate to null and another expression otherwise, you can use the null-coalescing operator:

specific_tax ?? default_tax

This will return the value of specific_tax if specific_tax is not null, otherwise it returns default_tax.

List fields

Fields of type list are expressed as arrays. Having a field positions consisting of list entries with child fields product, unit_price and quantity, we can express the sum position_total of one single list entry to be:

unit_price * quantity

Imagine then that we have the following positions, here illustrated in the form (product, position_total): (Donut, 4.50), (Pretzel, 2.80) and (Bread, 6.00). Applying the following recipe:

positions[0].position_total

returns 4.50. But we can do more, because instead of specifying the index inside [], writing something like…

positions[*].position_total

will give us the following result:

[4.5, 2.8, 6.0]

This is called wildcard expression, because it maps each entry in the list to whatever is defined after .. Here, each position in the list is mapped to position.position_total.

If we now want to sum all position_total fields of the positions list, we can write

sum(positions[*].position_total)

which gives us 13.30.

Arrow functions

Some functions can also accept functions as input, which are defined in the form of (a, b, ..., c) => y where a, b, ..., c) are parameters andy is the return value. This is especially useful when we want to filter a list by some criterium:

[1, 2, 3, 4, 5].filter((a) => a > 3)

which yields [4, 5].