WindoorERP Documentation 19.0

Configure Price Engines: formula-driven pricing, conditional rules and amount distribution

27 min read Updated 2026-08-29 WindoorERP 19.0
This article is step 13 of 18 in 3- Setup CRM and Sales

What this does

A Price Engine is a saved pricing formula. Instead of typing a unit price on every quotation line, you write the arithmetic once — using values read live from the product, the order, the line and the customer — and the system fills the price in and keeps it correct while the line is edited.

One engine can hold more than one formula: a base formula, plus ordered conditional rules that switch to a different formula when a field matches (a customer country, a product category, a quantity). Engines are approved before anyone can use them, every change is versioned, and any calculated line can explain how its price was reached.

This article walks through the workflow first, then documents every tab, every field and every wizard in the order you meet them.

Before you start

  • You need the Price Engine Manager role to create formulas. Price Engine User only reads engines and runs the order-side wizards; Price Engine Administrator can additionally delete engines and import formulas.
  • Decide who approves. The settings are described under Settings below.
  • The products whose price should be formula-driven. You point them at the engine in the last step.

Settings

Open Sales › Configuration › Settings and find the Price Engine section. Two settings live there, and both are stored system-wide rather than per user.

FieldWhat it does
ApproverThe user allowed to press Approve on an engine. Any Price Engine Manager can also approve, so this field is about naming one specific person on top of the role — for example a commercial director who is not a manager of the pricing team.
ModelsA comma-separated list of the models a formula variable, or a conditional rule, is permitted to read from. The default is sale.order, sale.order.line, product.product, product.template, res.partner. Removing a model from this list immediately removes it from the pickers on the Variables and Conditional Rules tabs.

How a price is produced

  1. 01
    A product is added to a quotation line.
  2. 02
    The product's Price Engine is copied onto the line — but only if that engine is approved, inside its date window, allowed for this customer and in the same company. If any of those fail, no engine is attached and the ordinary sales price stands.
  3. 03
    The engine chooses which formula to run: conditional rules are evaluated in sequence and the first match wins; with no match, the base formula is used.
  4. 04
    Every {{variable}} in that formula is replaced by the live value it is mapped to, and the expression is evaluated in a restricted sandbox.
  5. 05
    The result is written to the line's Unit Price, and re-evaluated whenever a tracked field changes.

Build your first engine

  1. Go to Sales › Configuration › Price Engines and click New.
  2. Enter a Name that says what it prices, for example Volume Discount — Aluminium Profiles.
  3. On the Formula tab, type the expression. Write each input as {{name}}, for example {{product_price}} * (1 - {{discount}} / 100).
  4. Open the Variables tab. Every {{name}} you typed is already listed — map each one to a field, or to a fixed number, and give it a Sample Value.
  5. Click Test Formula. The Test Result under the formula shows what the sample values produce, or the error that stops it.
  6. Optionally restrict the engine to a date window or to named customers on the Restrictions tab.
  7. Click Submit for Approval, then have the approver click Approve. Only approved engines can be selected on an order line.

The Price Engines list

The list at Sales › Configuration › Price Engines colours each row by state: blue for draft, amber for pending approval, green for approved. Drag the handle at the left of a row to reorder.

ColumnMeaning
(handle)Drag to change Sequence. It orders this list only; it does not decide which engine wins on a line.
NameThe engine's name.
CompanyHidden by default. Add it with the (adjust settings) icon at the right of the column titles when you run more than one company.
StateDraft, Pending Approval or Approved, shown as a coloured badge.
Approved ByWho approved it, filled by the system.
Approved DateWhen, filled by the system.

The search view offers filters for Draft, Pending, Approved and Archived, and lets you search by name, state or company.

Price Engines list with state, approver and approval date

The engine form: header and top fields

The status bar at the top right shows Draft › Pending Approval › Approved. The buttons beside it appear according to state and role:

ButtonWhen it showsWhat it does
Submit for ApprovalDraftValidates the engine and moves it to Pending Approval.
ApprovePending ApprovalApproves it and stamps Approved By and Approved Date.
Reset to DraftAny state but Draft, managers onlyReturns it to Draft for editing. It asks for confirmation, and it clears the approval stamps.
Test FormulaAlwaysEvaluates the formula against the sample values and refreshes Test Result.

Three round buttons sit above the form:

ButtonWhen it showsWhat it does
Apply to ProductsApproved engines, managers onlyOpens the bulk apply wizard described below.
Export FormulaAlwaysDownloads the engine as a JSON file.
Import FormulaAdministrators onlyOpens the import wizard described below.

Under the title are the identifying fields:

FieldMeaning
NameRequired. What the engine prices — this is what a salesperson sees in the dropdown on the order line, so name it for them.
SequenceOrdering of the list view. Default 10.
ActiveUntick to archive the engine. An archived engine disappears from the list and from every picker, but stays on the orders that already used it.
CompanyPins the engine to one company. An engine can never price a line belonging to a different company — the line reports an error instead. Leave it empty to share the engine across companies.
Approved ByRead-only. The user who approved.
Approved DateRead-only. When approval happened.

Note

One engine, Product Sale Price, is shipped as the default. Its formula is simply {{product_price}}, it is already approved, it belongs to no company, and it cannot be edited or deleted. Every new product is stamped with it on creation, so a product with no special pricing still runs through the engine and simply returns its own sales price.

Price engine form: header buttons, formula body, preview, test result and the quick reference panel

Formula tab

The formula is one expression that must produce a number — the unit price. It is not a script: there are no loops, no assignments and no access to records beyond the variables you mapped. Anything outside the list below is refused when the formula is checked.

AllowedWhat you can write
Arithmetic+ add, - subtract, * multiply, / divide, ** power, ( ) to group
Functionsmin(a, b), max(a, b), round(x, decimals), abs(x), ceil(x), floor(x)
Comparisons>, >=, <, <=, ==, !=
Conditionalsvalue_if_true if condition else value_if_false, combined with and, or, not
Variables{{name}} — auto-detected and listed on the Variables tab

The tab itself holds four things:

FieldMeaning
FormulaThe expression itself, in a plain multi-line box. This is the base formula — the fallback used whenever no conditional rule matches.
Formula PreviewRead-only. The same expression reprinted in monospace on a grey panel, so a long formula stays readable while you edit above it.
Test ResultRead-only. What the formula returns when every variable is replaced by its Sample Value. It refreshes when you press Test Formula.
Test ErrorRead-only, shown in red and only when something is wrong — an unmapped variable, a division by zero, a forbidden expression.

Below them sits a permanent Quick Reference panel repeating the operators, the functions, the comparison symbols and two worked examples, so you do not have to leave the form to remember the syntax.

GoalFormula
Use the catalogue price unchanged{{product_price}}
Apply a percentage discount{{product_price}} * (1 - {{discount}} / 100)
Quantity break at 10 units{{product_price}} * (0.95 if {{qty}} >= 10 else 1.0)
Cost plus markup{{product_cost}} * (1 + {{markup}} / 100)
Never sell below a floormax({{product_price}} * 0.8, 50)
Margin that shrinks with volume{{product_cost}} * (({{base_margin}} - ({{qty_factor}} * {{qty}})) / 100)

Note

A formula is capped at 1000 characters and its result must be a finite number. Anything else — a division by zero, a non-numeric outcome — is reported as an error on the line instead of silently writing a wrong price.

Conditional Rules tab

Conditional rules let one engine price different situations differently, without maintaining a separate engine for each. Each rule is a single condition plus its own formula; rules are checked in Sequence order and the first one that matches is used. If none match, the base formula on the Formula tab is the fallback.

The tab opens as an editable list. Inactive rules are greyed out.

ColumnMeaning
(handle)Drag to reorder. Sequence is the priority.
ActiveUntick to park a rule without deleting it — useful for a promotion you will bring back.
NameA label for the rule. Defaults to Conditional Rule; rename it to something you will recognise in a price trace.
Condition SummaryRead-only. The condition written out in words, for example Customer › Country is equal to Qatar.
FormulaThe expression used when this rule matches.

Open a rule to reach its full form, grouped into Rule, Condition and Formula Branch:

FieldMeaning
SequenceThe priority number. Lower is checked first.
ActiveWhether the rule participates at all.
NameRequired. The rule's label.
ModelWhat kind of record to look at — sale order, sale order line, product, product template or contact, subject to the Models setting.
FieldThe field on that model to compare. Unlike variables, a rule may test text, yes/no, selection and link fields as well as numbers.
OperatorThirteen choices, listed below.
Comparison ValueThe value to compare against. The form shows a text, a number or a yes/no box depending on the field's type, and hides the box entirely for is set and is not set. Numeric operators are refused on text fields and vice versa.
Pick ValueA button, shown once a field is chosen. It opens a picker listing the real values behind that field so you never have to type a technical code.
ConditionRead-only. The same summary as in the list, so you can check the rule reads the way you meant.
Formula BranchThe expression to use when the rule matches, written exactly like the base formula and using the same variables.

The operators are: is equal to, is not equal to, is greater than, is greater than or equal to, is less than, is less than or equal to, contains, does not contain, is one of, is not one of, is set, is not set.

Example

Rule 10 — Customer › Country is equal to Qatar{{product_price}} * 0.90

Rule 20 — Order Line › Quantity is greater than or equal to 50{{product_price}} * 0.95

Base formula (no rule matched) → {{product_price}}

A Qatari customer ordering 60 units gets 10%, not 5%: rule 10 is checked first and wins.

Important

Sequence is the whole logic. Put the narrowest condition at the top; a broad rule placed first will shadow everything under it, and the engine will look "wrong" while behaving exactly as configured.

Restrictions tab

Two groups, Date-Based Pricing and Customer-Specific Pricing, decide when and for whom the engine may be attached to a line.

FieldMeaning
Date FromFirst day the engine may be used. Empty means no lower bound.
Date ToLast day the engine may be used. Empty means no upper bound — which is how a promotional price accidentally outlives its promotion.
Date StatusRead-only badge: Always Active when no dates are set, Upcoming before Date From, Currently Active inside the window, Expired after Date To.
Apply to All CustomersTicked by default. Untick it to restrict the engine to a named list.
CustomersShown only when the box above is unticked. The contacts the engine applies to — contract rates, distributor pricing, one negotiated account. Leaving it empty while the box is unticked means the engine applies to nobody.

Outside the window, or for a customer not on the list, the engine simply is not attached and the product's ordinary sales price is used. That is what makes a seasonal price expire by itself. The tab also carries a short built-in reminder of these rules under the two groups.

Note

The date checked is the order date of the quotation, not today's date, so re-opening an old order does not re-price it under a newer campaign.

Variables tab

Variables are detected from the formula text, so the list fills itself; you cannot add or delete rows here, only configure them. A blue banner at the top of the tab says as much. Click a row to open it.

FieldMeaning
VariableRead-only. The token as written in the formula, including the braces.
LabelA readable name for the variable, for the benefit of whoever reads the engine next.
TypeField reads a live value from a record; Fixed is a constant you type once.
ModelRequired for a field variable. Only models on the Models setting appear.
FieldRequired for a field variable. Only numeric fields — integer, decimal, monetary — can be picked, because the result has to be arithmetic.
Fixed ValueRequired for a fixed variable, and hidden for a field variable. A constant such as a standard markup.
Sample ValueUsed by Test Formula only. It never affects a real order.
Field PathRead-only. The full path the value is read from, so a mis-mapped variable is visible without opening an order.
Field LocationRead-only. Where that field physically lives, plus a warning when the mapping is questionable.
Tracking TypeRead-only badge set by the system: Real-time Tracking or No Tracking. See the table below.
(handle)Drag in the list to reorder variables. Presentation only.

Where a variable's field lives decides when the price refreshes:

Variable reads fromTrackingEffect
Order line (quantity, line discount…)Real-time TrackingThe price recalculates as soon as the field is edited on the line.
Order header (currency rate, customer…)No TrackingExisting lines keep their price until someone clicks Recalculate Prices.
Product or product template (sales price, cost…)No TrackingA catalogue change applies to new lines only; confirmed quotations keep the price they were sold at.

Description, Version History and Formula Examples tabs

Description is a free rich-text field. Use it for the commercial intent a formula cannot express: who negotiated this pricing, what it replaces, when it should be reviewed.

Version History is visible to managers and is entirely read-only — you cannot add, edit or delete a snapshot. One is written automatically whenever a formula changes, a conditional rule changes, or the engine moves between states.

FieldMeaning
DateWhen the snapshot was taken.
UserWho caused the change.
Change TypeFormula, Conditional Rule or Approval State.
NoteA short description, such as Submitted for approval.
State Before / State AfterHidden columns in the list, always present on the snapshot form. The states either side of the change.
Old Formula / New FormulaOn the snapshot's Formula page: the expression before and after.
Rules SnapshotOn the snapshot's Rules Snapshot page: the conditional rules exactly as they stood at that moment.
CompanyShown in multi-company databases.

Formula Examples is a read-only page of ready-made expressions with an explanation of each, generated by the system. It is a reference to copy from, not something you edit.

Below the tabs, the ordinary chatter records every state change, every approval and every message, with its author and timestamp.

Approval: Draft, Pending Approval, Approved

In Draft an engine is editable and invisible to sales. Submit for Approval moves it to Pending Approval; every variable must be mapped first, the base formula must evaluate cleanly, and every active conditional rule is re-checked against the sample values — so a rule that cannot evaluate is caught here rather than on a customer's quotation.

The approver — any Price Engine Manager, or the user named as approver in Settings — clicks Approve, which stamps Approved By and Approved Date. Only approved engines can be chosen on a sale order line, or set as a product's default.

Reset to Draft pulls a live engine back for editing and clears the approval stamps; it asks for confirmation first and is limited to managers. Changing the formula or the rules of an approved engine records a version snapshot before the change.

Attach an engine to products

One product at a time: open the product and set Price Engine — it sits just under Product Category on the product form, and offers approved engines only. Every product created from now on starts with the shipped Product Sale Price engine already in that field.

Many products at once: open the approved engine and click Apply to Products.

FieldMeaning
Price EngineThe engine being applied. Pre-filled from the engine you opened it on.
Selection ModeThree radio choices: Manual Selection, All Products in Categories, or All Products.
ProductsShown for manual selection. The picker lists each candidate with its category, its sales price and the engine it currently carries — so you can see what you are about to overwrite.
Product CategoriesShown for the category mode. Every product in the chosen categories is targeted.
(count)A live count of how many products the wizard is about to update, shown in the blue banner above the buttons.

Important

Applying replaces whatever engine those products already had. Read the count in the banner before clicking Apply; on the All Products mode it is the size of your whole catalogue.

On the quotation

The order line carries the calculation. Add these columns with the (adjust settings) icon at the right of the column titles if they are hidden:

FieldMeaning
Price EngineThe engine driving this line. Filled from the product, and only approved engines are selectable. Clearing it returns the line to the ordinary price.
Manual Price OverrideTick it and Unit Price becomes editable. While it is ticked the formula never touches the line again; that is what protects a negotiated price from the next recalculation. Unit Price is read-only whenever it is unticked.
Calculated PriceRead-only. What the formula currently returns, so you can compare it against the price actually on the line.
Price Engine ErrorRead-only. The last formula or rule error for this line, if any. On the line's own form it appears only when there is something to show.
Tracked FieldsRead-only. The fields whose editing triggers an automatic recalculation of this line.

Open a line in its own form and click Explain Price. The window that opens answers "why did this line price like that?" in one screen:

FieldMeaning
Order Line, Order, ProductWhat is being explained.
Price EngineThe engine that ran.
Matched RuleThe conditional rule that won, or empty when the base formula was used.
Current PriceThe unit price on the line right now.
Calculated PriceWhat the formula produces — a difference between the two means a manual override.
ErrorWhy the calculation failed, when it did.
Formula UsedThe exact expression that ran, base or branch.
VariablesEvery variable with the value it resolved to for this line.
Rule TraceOne row per conditional rule: the rule, its condition, the actual value found on this order, whether it matched, and the formula it would have used.

Recalculate Prices

The Recalculate Prices button under the order lines re-runs every line's engine — the way to pick up a change that real-time tracking does not catch, such as a new cost, a corrected formula, or a different customer on the order.

If no line carries a manual override it simply recalculates and tells you it is done. If any line does, a confirmation window opens first, reporting how many lines are protected and how many will be recalculated, and offering:

ButtonWhat it does
Recalculate Automated OnlyRe-prices the automated lines and leaves every manually overridden price exactly as it is.
Reset All and RecalculateClears the override flags and re-prices every line from its formula. Negotiated prices are lost, so use it deliberately.
CancelNothing is changed.

The window also reminds you of the middle path: untick Manual Override on the specific lines you want re-priced, then press the button again.

Note

Recalculate Prices and Distribute Amount both refuse to run on a locked or cancelled order, and their buttons are hidden there.

Distribute Amount

Distribute Amount solves the opposite problem: the total is agreed with the customer and the lines have to add up to it.

FieldMeaning
Current Total (Untaxed)Read-only. The order's untaxed total as it stands.
Target Total AmountRequired. The figure the lines must add up to. It opens pre-filled with the current untaxed total, so you overwrite it with the agreed number.
DifferenceRead-only. Target minus current, green when you are going up and red when you are going down.
Difference (%)Read-only. The same gap as a percentage.

Clicking Apply rewrites every line's unit price in proportion to its current subtotal. Rounding is done by largest remainder, so the distributed lines sum to the target exactly rather than landing a few units away from it. Section and note lines are skipped, as are lines with no quantity; a line discounted 100%, or an order whose total is zero, cannot be distributed and the wizard says so instead of guessing.

Important

Every distributed line is marked Manual Price Override, which is what stops the engines from immediately undoing the distribution. It also means a later Recalculate Automated Only will skip those lines, and Reset All and Recalculate will wipe the distribution.

Each run is recorded on the order's Price Distribution tab:

FieldMeaning
Last Distribution Date / Last Distribution UserRead-only summary at the top of the tab: when the order was last distributed and by whom.
Date, UserOn each history row: when it ran and who ran it.
Original Total, Target TotalThe before and after figures for that run.
Difference, Difference %The gap that run closed, in currency and as a percentage.
Line DetailsOn the row's form: the per-line record of old price, new price, quantity and subtotals, including which lines absorbed the rounding.
CompanyShown in multi-company databases.

The list cannot be created, edited or deleted by hand — it is an audit trail, kept alongside the note the distribution posts in the chatter.

Test before you roll out

Before an engine touches live pricing, open it and click Price Simulation. The filters decide what it replays:

FieldMeaning
From Date / To DateThe period of orders to test against. To Date defaults to today.
CustomersOptional. Limit the test to named customers.
ProductsOptional. Limit the test to named products.
Order StateQuotations, Sales Orders, or All — the default.

Press Run Simulation and the wizard reports totals — Total Lines, Current Total, New Total and Total Difference — over a line-by-line table:

ColumnMeaning
Order, Product, QuantityThe line being simulated.
Current Price, New PriceThe unit price today against what the engine would produce.
Matched RuleWhich conditional rule would have won on that line.
ErrorAny line the formula could not price — worth reading before anything else.
Formula UsedHidden by default. The exact expression that ran for that line.
Difference, Diff %The change per unit, in currency and as a percentage.
Current Subtotal, New Subtotal, Subtotal DifferenceThe same comparison at line level, which is where the commercial impact actually shows.

Simulating changes nothing: no order is touched, and you close the window when you are done. Reading a simulation before approval is the cheapest way to catch a rule whose sequence is wrong or a variable mapped to the wrong field.

Templates, import and export

Pre-built templates ship with the module — tiered volume discount, per-unit bulk discount, minimum margin guarantee, minimum order pricing, price floor, cost-plus markup, variable margin by quantity and more. They appear in the Price Engines list like any other engine. Open one, read how it is built, and duplicate it as the starting point for your own.

Export Formula downloads the engine as a JSON file carrying the name, description, base formula, every variable with its model and field mapping, and every conditional rule with its condition and branch formula.

Import Formula, available to administrators, takes that file back in.

FieldMeaning
JSON FileRequired. The exported file to load. The wizard refuses a file that is not valid JSON, is not UTF-8, or is missing the name or the formula.
FilenameFilled in by the upload.
PreviewRead-only. What the file contains, shown before you commit to importing it.

The wizard also carries short on-screen instructions. This is how a formula proven on one company or one database is moved without retyping it.

Pricing a window from its design

For fabricated openings, the design itself supplies the number to price on. When a WebCC design is analysed, it writes Design System Amount onto the sale order line: the sum, for one unit of that design, of each frame's area multiplied by the price rate per m² of the profile system (and colour) that frame resolves to. Those rates live on the profile system and on its colour mappings as Price Rate (per m²).

Because it is a numeric field on the order line, it can be mapped as a variable and used like any other:

Example

{{design_system_amount}} * (1 + {{margin}} / 100) — the area-based value of the design as drawn, plus the commercial margin.

Being an order line field, it tracks in real time: redraw the design, the area changes, the line re-prices.

Who can do what

RoleCan
Price Engine UserSee engines and their variables; use Recalculate Prices, Distribute Amount and Explain Price on orders. Cannot create or edit a formula.
Price Engine ManagerEverything above, plus create and edit engines, define variables and conditional rules, reset to draft, run simulations, apply to products, read the version history, and approve. Cannot delete engines.
Price Engine AdministratorEverything above, plus delete engines, manage the default engine, import formulas and change the settings.

Engines, rules, versions and distribution history are all company-scoped, so a multi-company database keeps each company's pricing to itself. The shipped default engine is the exception: it belongs to no company on purpose, and nobody may edit or delete it.

Troubleshooting

  • The engine is not offered on the line. It is not approved, or it is outside its date window, or the customer is not on its list, or it belongs to another company, or it has been archived.
  • The price does not move when I change the quantity. Either Manual Price Override is ticked on that line, or no variable of the engine reads an order line field, so nothing is tracked in real time.
  • "Please map all variables before submitting". A variable in the formula still has no field or fixed value on the Variables tab.
  • Division by zero, or an error shown on the line. A variable resolved to zero in a denominator. Guard it, for example {{amount}} / max({{qty}}, 1).
  • "Formula expression is not allowed". The expression used something outside the allowed operators and functions. Rewrite it with the vocabulary in the Formula tab table.
  • Approval is refused. The user is neither a Price Engine Manager nor the approver named in Settings.
  • "Price engine company does not match". The engine is pinned to one company and the order line belongs to another. Either clear the engine's Company field or build a separate engine per company.
  • The buttons under the order lines are missing. The order is locked or cancelled.

Common mistakes

  • Ordering conditional rules broadest-first, so the general rule matches before the specific one ever gets a chance.
  • Mapping a variable to a product field and then expecting confirmed orders to re-price themselves — catalogue changes reach new lines only.
  • Using Reset All and Recalculate on an order that carries negotiated line prices, or on one that has been distributed.
  • Approving an engine straight into production without a Price Simulation, then discovering the impact on the next batch of quotations.
  • Leaving a promotional engine with no Date To, so a seasonal price stays live after the season.
  • Editing an approved engine's formula for a one-off customer instead of adding a conditional rule for them.
  • Running Apply to Products in All Products mode to reach a handful of items, overwriting the engine on the rest of the catalogue.

Was this article helpful?

Running a window or door factory?

Ask for a demo