# Calculation Authoring

> A functional description of how an admin builds calculations — the named, product-scoped formulas that produce a number . This is the authoring/usage view: w…

Source: CBX documentation, version 4.0 preview (unreleased). Canonical page: https://docs.configbox.at/docs/4.0-preview/functional/calculation_authoring. Last updated 2026-08-05.

---
A **functional** description of how an admin builds **calculations** — the named, product-scoped
formulas that produce a number (a price, a recurring price, a weight, or a min/max bound). This is the
authoring/usage view: what you can express and how you build it. For *how* calculations are stored and
evaluated, see `technical/com_configbox_calculation_engine.md`.

> A calculation answers "given the customer's current selections, what number should this be?" It does
> nothing on its own — it only takes effect once **assigned** to a question/answer (as its price,
> recurring price or weight), used as a **validation bound** on a numeric question, **referenced by
> another calculation**, or **referenced by a rule** (the "Results of calculations" condition).

---

## 1. Creating a calculation

Each calculation belongs to **one product** and has:
- **Name** — how you'll recognize and reference it (e.g. "Area price", "Volume discount factor").
- **Product** — the product it applies to.
- **Type** — the authoring style: **Formula**, **Code**, or **Matrix** (below). The type determines
  which editor you get.

You typically create several small calculations and compose them (one calculation can reference
another), rather than one giant formula.

Picking the type comes down to the shape of the logic:

```mermaid
flowchart TD
    S{"What shape<br>is the logic?"}
    S --> F["Readable, reusable<br>arithmetic"] --> TF["Formula<br>(visual builder)"]
    S --> C["Quick math to type;<br>totals or several questions"] --> TC["Code<br>(expression)"]
    S --> M["A table keyed by<br>two inputs"] --> TM["Matrix<br>(lookup table)"]
```

## 2. Where a calculation is used (how it takes effect)

A calculation only matters once it's referenced. The places an admin assigns a calculation:
- **Question → Price / Recurring Price / Weight Calculation** — the question's contribution is
  computed by the calculation (instead of a static price).
- **Answer (Option Assignment) → Price / Recurring Price / Weight Calculation** — overrides the
  answer's price/weight for that question with a calculation.
- **Question → Calculated Minimum / Maximum Value** — a numeric/text question's allowed range is
  computed (e.g. "max length depends on the chosen material").
- **Inside another calculation** — as a building block (the "Calculation" term/macro).
- **Inside a rule** — the "Results of calculations" condition compares a calculation's result to a
  value (see the rule-authoring doc).

> The number a calculation returns is the **raw** value. Currency conversion, customer-group price
> overrides and tax are applied automatically afterwards by the pricing layer — you author in the base
> currency and don't handle tax in the formula.

All the attachment points at a glance:

```mermaid
flowchart LR
    CALC["Calculation<br>(returns a raw number)"]
    CALC --> P1["Question / answer:<br>price, recurring price or weight"]
    CALC --> P2["Question:<br>calculated min / max bound"]
    CALC --> P3["Another calculation<br>(as a building block)"]
    CALC --> P4["A rule<br>('Results of calculations' condition)"]
    P1 --> POST["Pricing layer afterwards:<br>currency, group overrides, tax"]
```

---

## 3. Authoring style A — Formula (visual builder)

A drag-and-drop editor where you assemble an arithmetic expression from **terms**. You drag terms
from type tabs into the formula area, arrange them left-to-right, and group with **brackets**.

> Verified live: the editor shows the **Number** box and **`+ − * /`** operator buttons directly, plus
> tabs labelled **Questions** (= Element Attribute), **Customer** (= Customer Group), **Calculations**,
> and **Functions**. The Questions tab has a page filter + title search and a **"Regarding Question"**
> entry; each placed term shows an inline **"or [fallback]"** box; and the form lists where the
> calculation is **in use** (e.g. "Answer price calculation – 5/6/7").

Available term types (the building blocks):

- **Number** — a literal value you type (e.g. `12`, `0.25`).
- **Operator** — `+`, `−`, `×`, `÷` and parentheses. These connect the other terms; normal arithmetic
  precedence applies, and brackets force grouping.
- **Element Attribute** — a value taken from a **question** in the same product. You pick the question
  and which attribute:
  - **Entry in …** — the value the customer entered/selected (for number/slider/dropdown questions).
  - **Price of …** / **Recurring Price of …** — that question's computed price.
  - **Question custom field in …** — one of the question's custom fields.
  - **Answer custom field in …** / **Global answer custom field in …** — a custom field of the
    *selected answer* (assignment-level or option-level).
  - You can also reference **the question this calculation is assigned to** ("regarding" element), so
    one calculation can be reused on many questions and always read "its own" question.
  - A **fallback value** is used when the question has no value.
- **Calculation** — the result of *another* calculation in the product (lets you compose; e.g. a
  "base area" calc reused by a "price" calc).
- **Customer Group field** — a value from the current customer's group (a group custom field), so a
  formula can vary by customer segment.
- **Function** — a function applied to its parameters (each parameter is itself a little formula you
  drop terms into):
  - **Round** — round a number (optionally to N decimal places).
  - **Lowest value (min)** / **Highest value (max)** — pick the smallest/largest of 2–4 values.
  - **Custom functions** — additional functions a developer has registered (see the technical doc);
    they appear in the Functions tab with named parameter slots.

**Worked example** — price = (width × height in m²) × €65, rounded to 2 decimals, where width and
height are slider questions:

> `Round(` **Entry in "Width" × Entry in "Height" ÷ 1000000 × 65** `, 2 )`

You'd drop a Round function, and inside its first parameter build
`[Entry in Width] [×] [Entry in Height] [÷] [Number 1000000] [×] [Number 65]`, with `2` in the second
parameter.

The same example as the terms you'd actually place:

```mermaid
flowchart LR
    subgraph P1["Round — parameter 1 (a little formula of dropped terms)"]
        direction LR
        A["Entry in 'Width'"] --- X1(("×")) --- B["Entry in 'Height'"] --- D1(("÷")) --- C["Number<br>1000000"] --- X2(("×")) --- E["Number<br>65"]
    end
    P1 --> R["Round to 2 decimals<br>(parameter 2: Number 2)"] --> OUT["Result:<br>the question's price"]
```

Use Formula when the logic is arithmetic and benefits from being readable/reusable.

---

## 4. Authoring style B — Code (expression)

A free-text expression for when a formula is faster to type than to drag. You write normal math using:

- **Placeholders A–D** — each bound (in the form) to a **question**; at runtime each becomes that
  question's entered value. (Set "Question for placeholder A/B/C/D".)
- **Macros**:
  - **Total** / **TotalRecurring** — the running product total (regular / recurring).
  - **QuestionSelection(<questionId>)** — a question's entered value.
  - **QuestionPrice(<questionId>)** / **QuestionPriceRecurring(<questionId>)** — a question's price.
  - **Calculation(<calcId>)** — another calculation's result.
  - **QuestionProperty(…)** / **RegardingQuestion(…)** — attribute access / the assigned question.
- Standard operators and parentheses.

> **Legacy spellings:** CBX 3.x called these `ElementEntry()`, `ElementPrice()`,
> `ElementPriceRecurring()`, `ElementAttribute()` and `RegardingElement()`. The rename is a **hard
> one — there are no aliases**; the 3.5.2 update rewrote stored calc code in place, and any code
> arriving from outside (a customization, a hand-written import) has to be rewritten too. See
> [`migration-to-cb4/element-question-rename.md`](https://docs.configbox.at/docs/4.0-preview/migration-to-cb4/element-question-rename).

**Worked examples:**
- Area pricing (A = width mm, B = height mm): `A * B / 1000000 * 65`
- A surcharge of 25% of the order: `Total * .25`
- Combine: `Calculation(42) + QuestionPrice(37) * 2`

The question's **Price Multiplicator** (set on the question) is applied to the code result, so you can
keep the code per-unit and multiply by quantity at the question level.

Use Code for quick mathematical expressions, especially ones referencing totals or several questions.

---

## 5. Authoring style C — Matrix (lookup table)

A 2-D **lookup table** for "price depends on the combination of two inputs" (e.g. size × material,
weight × zone). You define:

- **Column input** and **Row input**, each either a **Question** (the customer's selection/value is
  the key) or another **Calculation** (its result is the key). (Set "…parameter type" = Question or
  Calculation, then pick the question/calc for rows and columns.)
- **The grid** — the cell values, which you can type in or **import from an `.xls`/`.xlsx`
  spreadsheet** (handy for large tables).
- **Lookup Value** (matching mode): **Exact Value**, **Next higher value**, or **Next lower value** —
  how an input that doesn't exactly hit a row/column header is matched (e.g. round up to the next size
  bracket).
- **Round Values to** — snap the inputs to a step before matching (for numeric inputs).
- **Multipliers** — the looked-up cell can be multiplied by a **static multiplicator**, by a
  **question selection** (a quantity question), and/or by a **calculated multiplier** (another
  calculation).

How a matrix turns its two inputs into a number:

```mermaid
flowchart LR
    RI["Row input<br>(question or calculation)"] --> SNAP["Snap to step<br>('Round Values to')"]
    CI["Column input<br>(question or calculation)"] --> SNAP
    SNAP --> MATCH{"Match row/column headers:<br>exact / next higher / next lower"}
    MATCH --> CELL["Looked-up<br>cell value"]
    CELL --> MULT["Apply multipliers:<br>static, quantity question,<br>calculated multiplier"]
    MULT --> RES["Result"]
```

**Worked example** — "Glass price" matrix: columns = "Width bracket", rows = "Thickness", cells = €/m²;
lookup = Next higher value so an in-between width uses the next bracket up; multiply the cell by the
"Quantity" question.

Use Matrix when pricing is naturally a table and doesn't follow a clean formula.

---

## 6. Composition, "regarding", and reuse

- **Compose** small calculations: a calculation can reference others (formula "Calculation" term,
  code `Calculation(id)`, or a matrix row/column input). Build a library of named pieces.
- **"Regarding" element** lets one calculation be assigned to many questions/answers and always read
  the value/price of *the question it's attached to* — so you don't need a separate calc per question.
- Calculations are **product-scoped**; copying a product copies its calculations and re-points all the
  internal references automatically.

A typical small library, composed:

```mermaid
flowchart LR
    A["'Base area'<br>(Formula)"] --> C["'Panel price'<br>(Formula)"]
    B["'Material factor'<br>(Matrix)"] --> C
    C --> Q["Assigned as the<br>'Panel' question's price"]
```

## 7. Element attributes you can reference (quick list)

When a formula/code reads from a question, the available attributes are: the **entered value**, the
**price** and **recurring price**, the question's **custom fields**, and the **selected answer's**
custom fields (assignment-level and global-option-level). Custom-field labels come from global
Configuration, so they appear under your own names.

## 8. Custom functions (extension)

Beyond Round/Min/Max, a developer can register additional functions (e.g. `ceilTo`, an external price
lookup) that then appear in the Formula editor's **Functions** tab with named parameter slots — so
admins can use them like built-ins. (How to add them: technical calc-engine doc.)

## 9. Practical notes

- **Calculations run on every selection change** (live pricing), so keep them reasonably simple;
  deeply nested calc graphs cost performance.
- **Author in the base currency, ignore tax** — conversion and tax happen after the result.
- **Numeric results** — calculations are expected to return numbers (used as money/weight/bounds).
- **Validation bounds** — a "Calculated Minimum/Maximum Value" on a question lets allowed input depend
  on other selections (e.g. max quantity depends on chosen package).

---

## 10. Notes for the refactor (functional observations)

- **Three authoring styles for one concept.** Formula, Code and Matrix all produce "a number from
  selections." A refactor could unify them behind one calculation concept with interchangeable editors
  (visual / expression / table) rather than three separate types.
- **"Regarding" + reuse is valuable** — the ability to write one calculation usable across many
  questions should be preserved (it's what keeps configurators maintainable).
- **Composition is a strength** — calculations referencing calculations form a small dependency graph;
  making that graph explicit (and cycle-checked) would help.
- **Matrix is really "tabular pricing"** with lookup semantics (exact/next-higher/next-lower) +
  multipliers + Excel import — a well-scoped feature worth keeping as a first-class option.
- **Regular vs. recurring** are parallel everywhere; consider modeling "price kinds" generically.
- Raw-number output with **currency/tax applied afterwards** is the right separation to keep.
