Next stop HeavyDeals speaks at the China HVAC&R International Trade Summit · Hangzhou · 22 OctHVAC&R Summit · Hangzhou · 22 Oct

Odoo and metal-indexed price lists: what pricelists can and can’t do

What an Odoo 19 pricelist rule can calculate, read from its code, where a copper or aluminium index does not fit, and a 12-point check for your own system.

HeavyDeals Published 7 min Checklist

An Odoo 19 pricelist prices well from cost: markup, rounding, a fixed surcharge, minimum and maximum margins, quantity breaks, dates, currency conversion and a pricelist for each customer. What it cannot do on its own is tie part of a product’s cost to an external metal index, such as the LME copper price, and recalculate when that index moves: a rule’s base can only be the sales price, the cost or another pricelist. Closing that gap takes a custom module or a scheduled job that updates the cost.

Check your system against Odoo 19

Checklist

Can your pricelist do this?

Twelve things a metal-indexed price list needs. The Odoo 19 column is already filled in from its code; fill in the last one for your own system.

Odoo 19 standard

3 yes · 3 with configuration · 6 no / custom development

Your system

0 yes · 0 with configuration · 0 no / custom development

0 of 12 answered
  1. 01
    Metal share of each product’s cost A pricelist rule has no field for it. Its inputs are a base, a discount or markup, rounding, a fixed surcharge, a minimum and maximum margin, a minimum quantity and dates. Inferred from the code
    Odoo 19 standard No / custom development
  2. 02
    Reference index and the date the list was built on Nothing in the pricelist stores an external index or its value when the list was set. The base can only be the sales price, the cost or another pricelist. Inferred from the code
    Odoo 19 standard No / custom development
  3. 03
    Automatic recalculation when the index moves Prices are computed from the base each time they are asked for, so they follow the cost. The missing step is turning an index move into a new cost: a custom module or a scheduled job. Inferred from the code
    Odoo 19 standard No / custom development
  4. 04
    Cost in US dollars, price in euros A rule converts its base into the pricelist currency at the rate of the date. Product cost is held in the company currency, so a dollar base needs its own USD pricelist underneath. Odoo documents automatic rate updates (daily, weekly or monthly) in the Accounting settings. Read in the code
    Odoo 19 standard With configuration
  5. 05
    A discount for each customer Each customer’s contact form has a pricelist field, and a rule can take another pricelist as its base and apply a discount on it. Documented by Odoo
    Odoo 19 standard Yes
  6. 06
    Quantity tiers that never contradict each other Tiers are native: each rule has a minimum quantity and the highest one is checked first. Nothing checks that a bigger tier gives a lower price; the validations cover dates, margins, loops between pricelists and the product. Inferred from the code
    Odoo 19 standard With configuration
  7. 07
    Rounding Rounds to a multiple of a value, after the discount and before the surcharge. Odoo’s own help text: round to 10.00 and add −0.01 to end in 9.99. Documented by Odoo
    Odoo 19 standard Yes
  8. 08
    An effective date for each price Each rule has a start and an end date and only applies between them. Documented by Odoo
    Odoo 19 standard Yes
  9. 09
    Approval before a change is published The pricelist has no status field. A saved rule applies as soon as its dates are current. A future start date schedules a change; it does not approve it. Inferred from the code
    Odoo 19 standard No / custom development
  10. 10
    An audit of how each price was calculated Partly. On a sales line Odoo works out which rule priced it, and each formula rule shows a worked example on 100. The cost and rate behind each price are not kept with it, and edits to rules are not tracked in the history. Inferred from the code
    Odoo 19 standard No / custom development
  11. 11
    Each customer sees their own prices online The customer’s pricelist sets the prices they see in the eCommerce app (website_sale). The standard customer portal lists quotes and orders (/my/quotes, /my/orders), not a price catalog. Read in the code
    Odoo 19 standard With configuration
  12. 12
    A notice to customers when their prices change Nothing in the pricelist code writes to customers when a rule changes. The pricelist’s own history tracks its currency, company and country groups, not its rules. Inferred from the code
    Odoo 19 standard No / custom development

“Documented by Odoo”: Odoo’s 19.0 documentation states it and the code agrees. “Read in the code”: the cell rests on a field or a line of Odoo 19.0 (Community) we read on 11 October 2026. “Inferred from the code”: it rests on something we did not find there, which is our reading, not a statement by Odoo. Standard means without custom modules. Your answers stay in this browser only.

Of the twelve points, Odoo 19 standard meets three out of the box, three with configuration and six only with custom development. The six it misses are exactly the ones that turn a price list into an indexed one: the metal share, the reference, the recalculation, the approval, the audit trail and the notice to customers. All of it comes from reading the code of the 19.0 branch on 11 October 2026 (commit e4e2d65) and Odoo 19’s official documentation; what we mark as inferred is what we did not find there.

How an Odoo pricelist rule calculates

In Odoo a pricelist is a set of rules. According to product_pricelist_item.py, each rule computes a price in one of three ways: a fixed price, a discount or a formula. With a formula, the price comes out like this:

base  = sales price | cost | other pricelist
price = base × (1 − discount %)
price = round to a multiple
price = price + surcharge
price = max(price, base + min margin)
price = min(price, base + max margin)

The base is first converted into the pricelist currency at the rate of the date, and a negative discount is a markup.

The rule that applies is the first one that matches, in this order: variant rules first, then product, category and global rules; within each group, the one with the highest minimum quantity. Only rules whose start and end dates cover the calculation date count; on an order, that is the order date (product_pricelist.py). The price is not stored: it is calculated every time it is needed. And, as Odoo 19’s pricelist documentation notes, a pricelist suggests a price that can always be overridden on the sales order.

What Odoo does well

  • Price from cost. A rule based on cost with a markup gives the selling price; if the cost changes, the next quote already has the new price, because it is calculated at that moment.
  • Rounding and margins. Rounding to a multiple happens after the discount and before the surcharge; Odoo’s documentation explains how to end in 9.99 (round to 10, surcharge of −0.01). Minimum and maximum margins put a floor and a ceiling on the result.
  • Tiers and dates. Each rule has a minimum quantity, a start date and an end date.
  • A pricelist per customer. Each customer has a pricelist assigned (res_partner.py), and a pricelist can be based on another one with a discount on top.
  • Currencies. The base is converted into the pricelist currency at the rate of the calculation date, and Odoo documents automatic rate updates, daily, weekly or monthly, from the Accounting settings.

Where the metal index does not fit

The formula of a metal-indexed price list needs three inputs an Odoo rule does not have: the share of the cost that follows the metal, the index value the list was set on, and today’s value.

factor = metal_share × index_today / index_ref + (1 − metal_share)
cost_€ = cost_ref_USD × factor / EURUSD_today

An Odoo rule calculates base × factor + surcharge, and the base arrives ready-made. Any index move has to be in the base before the rule reads it, and nothing in the pricelist code puts it there (inferred). An example with illustrative figures: if the index goes from 13,500 to 14,738 USD/t and 70% of the cost is metal, the cost rises 6.4% (0.7 × 14,738 / 13,500 + 0.3 = 1.064). The Odoo rule returns yesterday’s price until someone changes the cost.

There is a second catch, with currency. A product’s cost is stored in the company currency (product_template.py), which for a eurozone company is the euro. If you buy in dollars, the conversion happens when the cost is recorded, not when the price is calculated (inferred); for today’s rate to reach the price you need a base pricelist in USD.

Three ways to close the gap

  1. A scheduled job that rewrites the cost. It reads the index, calculates each product’s indexed cost and writes it into the cost field; rules based on cost follow it. This is custom code. And the cost field also values the inventory: its help text says it is computed automatically under average cost (AVCO) and used to value the product and compute margins, so putting the selling formula there affects valuation (inferred).
  2. A base pricelist in dollars that is re-imported. You keep fixed USD prices, recalculate them outside (in a spreadsheet) when the index moves and import them; the customers’ euro pricelists are based on it. Odoo handles the currency conversion natively; the index is still a manual step.
  3. A module that extends the rule calculation. The code leaves the door open: the calculation passes parameters “available for overrides”, and the label of the base is built so other modules can add options. A module can add an “index” base with its reference and its metal share. It has to be built and maintained for each Odoo version (inferred).

None of the three adds an approval step or a customer notice by itself: the pricelist model has no status, and its history does not record the rules.

How HeavyDeals works alongside Odoo

HeavyDeals is the commercial system for industrial distribution and works alongside your ERP: accounting, purchasing and the warehouse stay where they are. In HeavyDeals every price is a formula with the LME copper index, the EUR/USD rate and the margin, with each customer’s discount; when the index moves, the system proposes the recalculation and nothing is published until someone approves it. Each customer sees their net prices in their own portal and downloads their price list as a PDF with the dated quotes. You will find the formula step by step in the metal-indexed price list guide, and the other guides are in the guides index. See how it works.

Frequently asked questions

Can an Odoo pricelist follow the LME copper price?

Not on its own. In Odoo 19 a pricelist rule can only start from the sales price, the product cost or another pricelist, and it has no field for an external index or for the share of the cost tied to the metal. Following an index takes a custom module or a scheduled job that updates the cost.

What can an Odoo pricelist formula do?

It takes a base, applies a discount (a negative discount is a markup), rounds to a multiple of a value, adds a fixed surcharge and keeps the result between a minimum and a maximum margin over the base. Each rule can apply to a variant, a product, a category or everything, from a minimum quantity and between two dates.

Does Odoo convert a dollar cost into a euro price?

A rule converts its base into the pricelist currency at the rate of the date. The product cost is held in the company currency, so for a base in dollars you need a USD pricelist underneath the euro one.

Do Odoo pricelists have an approval step before a change goes live?

Not in the pricelist model of Odoo 19: it has no status field, and a saved rule applies as soon as its dates are current. A future start date schedules a change but does not approve it. This is our reading of the code.

Sources

  1. Pricelists (Odoo 19.0 documentation) Odoo · consulted 11 Oct 2026
  2. Multi-currency system (Odoo 19.0 documentation) Odoo · consulted 11 Oct 2026
  3. addons/product/models/product_pricelist_item.py (branch 19.0) Odoo, GitHub · consulted 11 Oct 2026
  4. addons/product/models/product_pricelist.py (branch 19.0) Odoo, GitHub · consulted 11 Oct 2026
  5. addons/product/models/product_template.py (branch 19.0) Odoo, GitHub · consulted 11 Oct 2026
  6. addons/product/models/res_partner.py (branch 19.0) Odoo, GitHub · consulted 11 Oct 2026
  7. addons/website_sale/models/website.py (branch 19.0) Odoo, GitHub · consulted 11 Oct 2026
  8. addons/sale/controllers/portal.py (branch 19.0) Odoo, GitHub · consulted 11 Oct 2026

Every figure and rule was checked against the sources above, on the date shown next to each; last review of this guide: 11 October 2026. The examples use illustrative figures, not today’s quotes. This is not legal or tax advice. Written with the help of AI and reviewed by the HeavyDeals team.

Get new guides by email

We write when we publish a guide or when a rule we cover changes (CBAM, anti-dumping, duties). Nothing else.

Unsubscribe by replying to any of the emails. Privacy · Or follow them by RSS

Your metal-indexed price list recalculates when the market moves; your team just approves it.

Book a demo

We only use your details to deal with your request. Privacy policy